Improved support for international text
- Implementation by @herm for GSOC 2012 (http://mapnik.org/news/2012/10/06/gsoc2012-status9/) - C++11 port, improvements, optimizations by @artemp - Testing and integration with master by @springmeyer - Thank you to all the support from @behdad along the way - Thanks for help testing @toton6868, @stephankn, @nirvn, @mfrasca, @simonsonc and many others Refs: #2073,#2070,#2038,#2037,#1953,#1820,#1819,#1714,#1634,#1547,#1532,#1319,#1208,#1154,#1146
This commit is contained in:
parent
622f95ec28
commit
64d5153aea
68 changed files with 4067 additions and 2824 deletions
|
@ -83,6 +83,7 @@ pretty_dep_names = {
|
||||||
'png':'PNG C library | configure with PNG_LIBS & PNG_INCLUDES',
|
'png':'PNG C library | configure with PNG_LIBS & PNG_INCLUDES',
|
||||||
'webp':'WEBP C library | configure with WEBP_LIBS & WEBP_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/',
|
'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/',
|
'z':'Z compression library | more info: http://www.zlib.net/',
|
||||||
'm':'Basic math library, part of C++ stlib',
|
'm':'Basic math library, part of C++ stlib',
|
||||||
'pkg-config':'pkg-config tool | more info: http://pkg-config.freedesktop.org',
|
'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_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),
|
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),
|
('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'),
|
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_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),
|
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
|
# Adding the required prerequisite library directories to the include path for
|
||||||
# compiling and the library path for linking, respectively.
|
# 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]
|
inc_path = env['%s_INCLUDES' % required]
|
||||||
lib_path = env['%s_LIBS' % required]
|
lib_path = env['%s_LIBS' % required]
|
||||||
env.AppendUnique(CPPPATH = os.path.realpath(inc_path))
|
env.AppendUnique(CPPPATH = os.path.realpath(inc_path))
|
||||||
|
@ -1220,6 +1222,7 @@ if not preconfigured:
|
||||||
REQUIRED_LIBSHEADERS = [
|
REQUIRED_LIBSHEADERS = [
|
||||||
['z', 'zlib.h', True,'C'],
|
['z', 'zlib.h', True,'C'],
|
||||||
[env['ICU_LIB_NAME'],'unicode/unistr.h',True,'C++'],
|
[env['ICU_LIB_NAME'],'unicode/unistr.h',True,'C++'],
|
||||||
|
['harfbuzz', 'harfbuzz/hb.h',True,'C++']
|
||||||
]
|
]
|
||||||
|
|
||||||
OPTIONAL_LIBSHEADERS = []
|
OPTIONAL_LIBSHEADERS = []
|
||||||
|
|
|
@ -285,11 +285,6 @@ class _Color(Color,_injector):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "Color(R=%d,G=%d,B=%d,A=%d)" % (self.r,self.g,self.b,self.a)
|
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):
|
class _Symbolizers(Symbolizers,_injector):
|
||||||
|
|
||||||
def __getitem__(self, idx):
|
def __getitem__(self, idx):
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
#include <mapnik/expression_node.hpp>
|
#include <mapnik/expression_node.hpp>
|
||||||
#include <mapnik/value_error.hpp>
|
#include <mapnik/value_error.hpp>
|
||||||
#include <mapnik/marker_cache.hpp> // for known_svg_prefix_
|
#include <mapnik/marker_cache.hpp> // for known_svg_prefix_
|
||||||
|
#include <mapnik/pixel_position.hpp>
|
||||||
|
|
||||||
using mapnik::symbolizer;
|
using mapnik::symbolizer;
|
||||||
using mapnik::rule;
|
using mapnik::rule;
|
||||||
|
@ -66,28 +66,26 @@ using mapnik::path_expression_ptr;
|
||||||
using mapnik::guess_type;
|
using mapnik::guess_type;
|
||||||
using mapnik::expression_ptr;
|
using mapnik::expression_ptr;
|
||||||
using mapnik::parse_path;
|
using mapnik::parse_path;
|
||||||
using mapnik::position;
|
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
using namespace boost::python;
|
using namespace boost::python;
|
||||||
|
|
||||||
tuple get_shield_displacement(const shield_symbolizer& s)
|
tuple get_shield_displacement(const shield_symbolizer& s)
|
||||||
{
|
{
|
||||||
position const& pos = s.get_shield_displacement();
|
mapnik::pixel_position const& pos = s.get_shield_displacement();
|
||||||
return boost::python::make_tuple(pos.first, pos.second);
|
return boost::python::make_tuple(pos.x, pos.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_shield_displacement(shield_symbolizer & s, boost::python::tuple arg)
|
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.x = extract<double>(arg[0]);
|
||||||
s.get_placement_options()->defaults.displacement.second = extract<double>(arg[1]);
|
s.get_placement_options()->defaults.displacement.y = extract<double>(arg[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
tuple get_text_displacement(const shield_symbolizer& t)
|
tuple get_text_displacement(const shield_symbolizer& t)
|
||||||
{
|
{
|
||||||
position const& pos = t.get_placement_options()->defaults.displacement;
|
mapnik::pixel_position const& pos = t.get_placement_options()->defaults.displacement;
|
||||||
return boost::python::make_tuple(pos.first, pos.second);
|
return boost::python::make_tuple(pos.x, pos.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_text_displacement(shield_symbolizer & t, boost::python::tuple arg)
|
void set_text_displacement(shield_symbolizer & t, boost::python::tuple arg)
|
||||||
|
|
|
@ -27,11 +27,13 @@
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
|
|
||||||
#include <mapnik/text/text_properties.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/text.hpp>
|
||||||
#include <mapnik/text/formatting/list.hpp>
|
#include <mapnik/text/formatting/list.hpp>
|
||||||
#include <mapnik/text/formatting/format.hpp>
|
#include <mapnik/text/formatting/format.hpp>
|
||||||
#include <mapnik/text/formatting/expression_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/text_symbolizer.hpp>
|
||||||
|
|
||||||
#include "mapnik_enumeration.hpp"
|
#include "mapnik_enumeration.hpp"
|
||||||
|
@ -96,7 +98,7 @@ public:
|
||||||
|
|
||||||
boost::python::tuple get_displacement(text_symbolizer_properties const& t)
|
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)
|
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 x = extract<double>(arg[0]);
|
||||||
double y = extract<double>(arg[1]);
|
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;
|
python_block_auto_unblock b;
|
||||||
this->get_override("apply")(ptr(&p), ptr(&feature), ptr(&output));
|
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"))
|
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);
|
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>
|
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"))
|
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);
|
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>
|
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"))
|
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);
|
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.
|
/* TODO: Add constructor taking variable number of arguments.
|
||||||
http://wiki.python.org/moin/boost.python/HowTo#A.22Raw.22_function */
|
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"))
|
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);
|
formatting::list_node::apply(p, feature, output);
|
||||||
}
|
}
|
||||||
|
@ -322,12 +324,12 @@ void insert_expression(expression_set *set, expression_ptr p)
|
||||||
set->insert(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;
|
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;
|
sym.get_placement_options()->defaults.format = format;
|
||||||
}
|
}
|
||||||
|
@ -395,7 +397,7 @@ void export_text_placement()
|
||||||
&text_symbolizer::set_placement_options)
|
&text_symbolizer::set_placement_options)
|
||||||
//TODO: Check return policy, is there a better way to do this?
|
//TODO: Check return policy, is there a better way to do this?
|
||||||
.add_property("format",
|
.add_property("format",
|
||||||
make_function(&get_format, return_value_policy<reference_existing_object>()),
|
make_function(&get_format),
|
||||||
&set_format,
|
&set_format,
|
||||||
"Shortcut for placements.defaults.default_format")
|
"Shortcut for placements.defaults.default_format")
|
||||||
.add_property("properties",
|
.add_property("properties",
|
||||||
|
@ -439,6 +441,7 @@ void export_text_placement()
|
||||||
.def_readwrite("largest_bbox_only", &text_symbolizer_properties::largest_bbox_only)
|
.def_readwrite("largest_bbox_only", &text_symbolizer_properties::largest_bbox_only)
|
||||||
.def_readwrite("text_ratio", &text_symbolizer_properties::text_ratio)
|
.def_readwrite("text_ratio", &text_symbolizer_properties::text_ratio)
|
||||||
.def_readwrite("wrap_width", &text_symbolizer_properties::wrap_width)
|
.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)
|
.def_readwrite("format", &text_symbolizer_properties::format)
|
||||||
.add_property ("format_tree",
|
.add_property ("format_tree",
|
||||||
&text_symbolizer_properties::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")
|
("CharProperties")
|
||||||
.def_readwrite_convert("text_transform", &char_properties::text_transform)
|
.def_readwrite_convert("text_transform", &char_properties::text_transform)
|
||||||
.def_readwrite_convert("fontset", &char_properties::fontset)
|
.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("text_opacity", &char_properties::text_opacity)
|
||||||
.def_readwrite("wrap_char", &char_properties::wrap_char)
|
.def_readwrite("wrap_char", &char_properties::wrap_char)
|
||||||
.def_readwrite("wrap_character", &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("fill", &char_properties::fill)
|
||||||
.def_readwrite("halo_fill", &char_properties::halo_fill)
|
.def_readwrite("halo_fill", &char_properties::halo_fill)
|
||||||
.def_readwrite("halo_radius", &char_properties::halo_radius)
|
.def_readwrite("halo_radius", &char_properties::halo_radius)
|
||||||
|
@ -486,24 +489,12 @@ void export_text_placement()
|
||||||
("TextPlacementInfo",
|
("TextPlacementInfo",
|
||||||
init<text_placements const*, double>())
|
init<text_placements const*, double>())
|
||||||
.def("next", pure_virtual(&text_placement_info::next))
|
.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("properties", &text_placement_info::properties)
|
||||||
.def_readwrite("scale_factor", &text_placement_info::scale_factor)
|
.def_readwrite("scale_factor", &text_placement_info::scale_factor)
|
||||||
;
|
;
|
||||||
register_ptr_to_python<std::shared_ptr<text_placement_info> >();
|
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,
|
class_<expression_set,
|
||||||
std::shared_ptr<expression_set>,
|
std::shared_ptr<expression_set>,
|
||||||
boost::noncopyable>
|
boost::noncopyable>
|
||||||
|
@ -551,7 +542,6 @@ void export_text_placement()
|
||||||
.def_readwrite_convert("text_opacity", &formatting::format_node::text_opacity)
|
.def_readwrite_convert("text_opacity", &formatting::format_node::text_opacity)
|
||||||
.def_readwrite_convert("wrap_char", &formatting::format_node::wrap_char)
|
.def_readwrite_convert("wrap_char", &formatting::format_node::wrap_char)
|
||||||
.def_readwrite_convert("wrap_character", &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("text_transform", &formatting::format_node::text_transform)
|
||||||
.def_readwrite_convert("fill", &formatting::format_node::fill)
|
.def_readwrite_convert("fill", &formatting::format_node::fill)
|
||||||
.def_readwrite_convert("halo_fill", &formatting::format_node::halo_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("text_opacity", &formatting::expression_format::text_opacity)
|
||||||
.def_readwrite("wrap_char", &formatting::expression_format::wrap_char)
|
.def_readwrite("wrap_char", &formatting::expression_format::wrap_char)
|
||||||
.def_readwrite("wrap_character", &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("fill", &formatting::expression_format::fill)
|
||||||
.def_readwrite("halo_fill", &formatting::expression_format::halo_fill)
|
.def_readwrite("halo_fill", &formatting::expression_format::halo_fill)
|
||||||
.def_readwrite("halo_radius", &formatting::expression_format::halo_radius)
|
.def_readwrite("halo_radius", &formatting::expression_format::halo_radius)
|
||||||
|
|
|
@ -97,12 +97,15 @@ public:
|
||||||
void pad(T padding);
|
void pad(T padding);
|
||||||
bool from_string(std::string const& str);
|
bool from_string(std::string const& str);
|
||||||
bool valid() const;
|
bool valid() const;
|
||||||
|
void move(T x, T y);
|
||||||
|
|
||||||
// define some operators
|
// define some operators
|
||||||
box2d_type& operator+=(box2d_type const& other);
|
box2d_type& operator+=(box2d_type const& other);
|
||||||
box2d_type& operator*=(T);
|
box2d_type& operator*=(T);
|
||||||
box2d_type& operator/=(T);
|
box2d_type& operator/=(T);
|
||||||
T operator[](int index) const;
|
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
|
// compute the bounding box of this one transformed
|
||||||
box2d_type operator* (agg::trans_affine const& tr) const;
|
box2d_type operator* (agg::trans_affine const& tr) const;
|
||||||
|
|
|
@ -33,6 +33,9 @@
|
||||||
#include <mapnik/image_compositing.hpp>
|
#include <mapnik/image_compositing.hpp>
|
||||||
#include <mapnik/font_engine_freetype.hpp>
|
#include <mapnik/font_engine_freetype.hpp>
|
||||||
#include <mapnik/gradient.hpp>
|
#include <mapnik/gradient.hpp>
|
||||||
|
#include <mapnik/text/text_properties.hpp>
|
||||||
|
#include <mapnik/text/placements_list.hpp>
|
||||||
|
#include <mapnik/text/glyph_info.hpp>
|
||||||
#include <mapnik/vertex.hpp>
|
#include <mapnik/vertex.hpp>
|
||||||
#include <mapnik/noncopyable.hpp>
|
#include <mapnik/noncopyable.hpp>
|
||||||
|
|
||||||
|
@ -317,9 +320,9 @@ public:
|
||||||
void translate(double x, double y);
|
void translate(double x, double y);
|
||||||
void save();
|
void save();
|
||||||
void restore();
|
void restore();
|
||||||
void show_glyph(unsigned long index, double x, double y);
|
void show_glyph(unsigned long index, pixel_position const& pos);
|
||||||
void glyph_path(unsigned long index, double x, double y);
|
void glyph_path(unsigned long index, pixel_position const& pos);
|
||||||
void add_text(text_path const& path,
|
void add_text(glyph_positions_ptr pos,
|
||||||
cairo_face_manager & manager,
|
cairo_face_manager & manager,
|
||||||
face_manager<freetype_engine> & font_manager,
|
face_manager<freetype_engine> & font_manager,
|
||||||
double scale_factor = 1.0);
|
double scale_factor = 1.0);
|
||||||
|
|
|
@ -24,15 +24,9 @@
|
||||||
#define MAPNIK_FONT_ENGINE_FREETYPE_HPP
|
#define MAPNIK_FONT_ENGINE_FREETYPE_HPP
|
||||||
|
|
||||||
// mapnik
|
// mapnik
|
||||||
#include <mapnik/debug.hpp>
|
#include <mapnik/config.hpp>
|
||||||
#include <mapnik/color.hpp>
|
|
||||||
#include <mapnik/utils.hpp>
|
|
||||||
#include <mapnik/box2d.hpp>
|
#include <mapnik/box2d.hpp>
|
||||||
#include <mapnik/ctrans.hpp>
|
|
||||||
#include <mapnik/geometry.hpp>
|
|
||||||
#include <mapnik/font_set.hpp>
|
#include <mapnik/font_set.hpp>
|
||||||
#include <mapnik/text/char_info.hpp>
|
|
||||||
#include <mapnik/image_compositing.hpp>
|
|
||||||
#include <mapnik/text_symbolizer.hpp>
|
#include <mapnik/text_symbolizer.hpp>
|
||||||
#include <mapnik/noncopyable.hpp>
|
#include <mapnik/noncopyable.hpp>
|
||||||
#include <mapnik/value_types.hpp>
|
#include <mapnik/value_types.hpp>
|
||||||
|
@ -40,90 +34,38 @@
|
||||||
|
|
||||||
// boost
|
// boost
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <boost/ptr_container/ptr_vector.hpp>
|
|
||||||
|
|
||||||
|
#include <boost/ptr_container/ptr_vector.hpp>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
#ifdef MAPNIK_THREADSAFE
|
#ifdef MAPNIK_THREADSAFE
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// stl
|
//// stl
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
|
||||||
|
|
||||||
struct FT_LibraryRec_;
|
struct FT_LibraryRec_;
|
||||||
|
|
||||||
namespace mapnik
|
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;
|
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
|
class MAPNIK_DECL freetype_engine
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static bool is_font_file(std::string const& file_name);
|
static bool is_font_file(std::string const& file_name);
|
||||||
|
|
||||||
/*! \brief register a font file
|
/*! \brief register a font file
|
||||||
* @param file_name path to 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.
|
* @return bool - true if at least one face was successfully registered in the file.
|
||||||
*/
|
*/
|
||||||
static bool register_font(std::string const& file_name);
|
static bool register_font(std::string const& file_name);
|
||||||
|
|
||||||
/*! \brief register a font file
|
/*! \brief register a font file
|
||||||
* @param dir - path to a directory containing fonts or subdirectories.
|
* @param dir - path to a directory containing fonts or subdirectories.
|
||||||
* @param recurse - default false, whether to search for fonts in sub directories.
|
* @param recurse - default false, whether to search for fonts in sub directories.
|
||||||
|
@ -137,11 +79,11 @@ public:
|
||||||
virtual ~freetype_engine();
|
virtual ~freetype_engine();
|
||||||
freetype_engine();
|
freetype_engine();
|
||||||
private:
|
private:
|
||||||
FT_LibraryRec_ * library_;
|
FT_LibraryRec_ *library_;
|
||||||
#ifdef MAPNIK_THREADSAFE
|
#ifdef MAPNIK_THREADSAFE
|
||||||
static std::mutex mutex_;
|
static std::mutex mutex_;
|
||||||
#endif
|
#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_;
|
static std::map<std::string, std::string> memory_fonts_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -149,7 +91,7 @@ template <typename T>
|
||||||
class MAPNIK_DECL face_manager : private mapnik::noncopyable
|
class MAPNIK_DECL face_manager : private mapnik::noncopyable
|
||||||
{
|
{
|
||||||
typedef T font_engine_type;
|
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:
|
public:
|
||||||
face_manager(T & engine)
|
face_manager(T & engine)
|
||||||
|
@ -157,74 +99,13 @@ public:
|
||||||
stroker_(engine_.create_stroker()),
|
stroker_(engine_.create_stroker()),
|
||||||
face_ptr_cache_() {}
|
face_ptr_cache_() {}
|
||||||
|
|
||||||
face_ptr get_face(std::string const& name)
|
face_ptr get_face(std::string const& name);
|
||||||
{
|
face_set_ptr get_face_set(std::string const& name);
|
||||||
face_ptr_cache_type::iterator itr;
|
face_set_ptr get_face_set(font_set const& fset);
|
||||||
itr = face_ptr_cache_.find(name);
|
face_set_ptr get_face_set(std::string const& name, boost::optional<font_set> fset);
|
||||||
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_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)
|
inline stroker_ptr get_stroker() { return stroker_; }
|
||||||
{
|
|
||||||
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_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
font_engine_type & engine_;
|
font_engine_type & engine_;
|
||||||
|
@ -232,31 +113,6 @@ private:
|
||||||
face_ptr_cache_type face_ptr_cache_;
|
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;
|
typedef face_manager<freetype_engine> face_manager_freetype;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,13 +22,73 @@
|
||||||
#ifndef MAPNIK_PIXEL_POSITION_HPP
|
#ifndef MAPNIK_PIXEL_POSITION_HPP
|
||||||
#define MAPNIK_PIXEL_POSITION_HPP
|
#define MAPNIK_PIXEL_POSITION_HPP
|
||||||
|
|
||||||
// Store a pixel position.
|
// stl
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
namespace mapnik
|
||||||
|
{
|
||||||
|
|
||||||
|
struct rotation;
|
||||||
struct pixel_position
|
struct pixel_position
|
||||||
{
|
{
|
||||||
double x;
|
double x;
|
||||||
double y;
|
double y;
|
||||||
pixel_position(double x_, double y_) : x(x_), y(y_) { }
|
pixel_position(double x_, double y_) : x(x_), y(y_) { }
|
||||||
pixel_position() : x(0), y(0) { }
|
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
|
#endif // MAPNIK_PIXEL_POSITION_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
|
bool get_unlock_image() const; // image is not locked to the text placement
|
||||||
void set_unlock_image(bool unlock_image);
|
void set_unlock_image(bool unlock_image);
|
||||||
void set_shield_displacement(double shield_dx, double shield_dy);
|
void set_shield_displacement(double shield_dx, double shield_dy);
|
||||||
position const& get_shield_displacement() const;
|
pixel_position const& get_shield_displacement() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool unlock_image_;
|
bool unlock_image_;
|
||||||
position shield_displacement_;
|
pixel_position shield_displacement_;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
34
include/mapnik/text/char_properties_ptr.hpp
Normal file
34
include/mapnik/text/char_properties_ptr.hpp
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* 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 CHAR_PROPERTIES_PTR_HPP
|
||||||
|
#define CHAR_PROPERTIES_PTR_HPP
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace mapnik
|
||||||
|
{
|
||||||
|
struct char_properties;
|
||||||
|
typedef std::shared_ptr<char_properties> char_properties_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CHAR_PROPERTIES_PTR_HPP
|
92
include/mapnik/text/dummy_shaper.hpp
Normal file
92
include/mapnik/text/dummy_shaper.hpp
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef MAPNIK_DUMMY_SHAPER_HPP
|
||||||
|
#define MAPNIK_DUMMY_SHAPER_HPP
|
||||||
|
|
||||||
|
// mapnik
|
||||||
|
#include <mapnik/text/text_properties.hpp>
|
||||||
|
#include <mapnik/text/text_line.hpp>
|
||||||
|
#include <mapnik/text/face.hpp>
|
||||||
|
// stl
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
namespace mapnik
|
||||||
|
{
|
||||||
|
|
||||||
|
struct dummy_shaper
|
||||||
|
{
|
||||||
|
|
||||||
|
/* Dummy shaping limitations:
|
||||||
|
* - LTR text only
|
||||||
|
* - Only first font in fontset is used
|
||||||
|
* - No kerning
|
||||||
|
* - No complex scripts
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void shape_text(text_line & line,
|
||||||
|
text_itemizer & itemizer,
|
||||||
|
std::map<unsigned,double> & width_map,
|
||||||
|
face_manager_freetype & font_manager,
|
||||||
|
double scale_factor )
|
||||||
|
{
|
||||||
|
unsigned start = line.first_char();
|
||||||
|
unsigned end = line.last_char();
|
||||||
|
mapnik::value_unicode_string const& text = itemizer.text();
|
||||||
|
size_t length = end - start;
|
||||||
|
if (!length) return;
|
||||||
|
line.reserve(length);
|
||||||
|
std::list<text_item> const& list = itemizer.itemize(start, end);
|
||||||
|
|
||||||
|
for (auto const& text_item : list)
|
||||||
|
{
|
||||||
|
face_set_ptr face_set = font_manager.get_face_set(text_item.format->face_name, text_item.format->fontset);
|
||||||
|
double size = text_item.format->text_size * scale_factor;
|
||||||
|
face_set->set_character_sizes(size);
|
||||||
|
if (face_set->begin() == face_set->end()) return; // Invalid face set
|
||||||
|
face_ptr face = *(face_set->begin());
|
||||||
|
FT_Face freetype_face = face->get_face();
|
||||||
|
|
||||||
|
for (unsigned i = text_item.start; i < text_item.end; ++i)
|
||||||
|
{
|
||||||
|
UChar32 c = text.char32At(i);
|
||||||
|
glyph_info tmp;
|
||||||
|
tmp.glyph_index = FT_Get_Char_Index(freetype_face, c);
|
||||||
|
if (tmp.glyph_index == 0) continue; // Skip unknown characters
|
||||||
|
tmp.char_index = i;
|
||||||
|
tmp.width = 0; // Filled in by glyph_dimensions
|
||||||
|
tmp.offset.clear();
|
||||||
|
tmp.face = face;
|
||||||
|
tmp.format = text_item.format;
|
||||||
|
face->glyph_dimensions(tmp);
|
||||||
|
width_map[i] += tmp.width;
|
||||||
|
line.add_glyph(tmp, scale_factor);
|
||||||
|
}
|
||||||
|
line.update_max_char_height(face->get_char_height());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mapnik
|
||||||
|
|
||||||
|
#endif // MAPNIK_DUMMY_SHAPER_HPP
|
|
@ -19,77 +19,35 @@
|
||||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*
|
*
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
#ifndef MAPNIK_FACE_HPP
|
||||||
|
#define MAPNIK_FACE_HPP
|
||||||
|
|
||||||
|
//mapnik
|
||||||
#ifndef MAPNIK_FONT_UTIL_HPP
|
#include <mapnik/text/glyph_info.hpp>
|
||||||
#define MAPNIK_FONT_UTIL_HPP
|
#include <mapnik/config.hpp>
|
||||||
|
|
||||||
// mapnik
|
|
||||||
#include <mapnik/noncopyable.hpp>
|
#include <mapnik/noncopyable.hpp>
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
// freetype2
|
// freetype2
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
#include <ft2build.h>
|
#include <ft2build.h>
|
||||||
#include FT_FREETYPE_H
|
#include FT_FREETYPE_H
|
||||||
#include FT_GLYPH_H
|
|
||||||
#include FT_STROKER_H
|
#include FT_STROKER_H
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//stl
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace mapnik
|
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// FT_Stroker wrapper
|
|
||||||
class stroker : mapnik::noncopyable
|
|
||||||
{
|
|
||||||
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
|
class font_face : mapnik::noncopyable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
font_face(FT_Face face)
|
font_face(FT_Face face);
|
||||||
: face_(face) {}
|
|
||||||
|
|
||||||
std::string family_name() const
|
std::string family_name() const
|
||||||
{
|
{
|
||||||
|
@ -101,45 +59,59 @@ public:
|
||||||
return std::string(face_->style_name);
|
return std::string(face_->style_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
FT_GlyphSlot glyph() const
|
|
||||||
{
|
|
||||||
return face_->glyph;
|
|
||||||
}
|
|
||||||
|
|
||||||
FT_Face get_face() const
|
FT_Face get_face() const
|
||||||
{
|
{
|
||||||
return face_;
|
return face_;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned get_char(unsigned c) const
|
double get_char_height() const;
|
||||||
{
|
|
||||||
return FT_Get_Char_Index(face_, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool set_pixel_sizes(unsigned size)
|
bool set_character_sizes(double size);
|
||||||
{
|
|
||||||
if (! FT_Set_Pixel_Sizes( face_, 0, size ))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool set_character_sizes(double size)
|
void glyph_dimensions(glyph_info &glyph) const;
|
||||||
{
|
|
||||||
if ( !FT_Set_Char_Size(face_,0,(FT_F26Dot6)(size * (1<<6)),0,0))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
~font_face()
|
~font_face();
|
||||||
{
|
|
||||||
FT_Done_Face(face_);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FT_Face face_;
|
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 // FACE_HPP
|
||||||
#endif // MAPNIK_FONT_UTIL_HPP
|
|
||||||
|
|
|
@ -24,16 +24,16 @@
|
||||||
|
|
||||||
// mapnik
|
// mapnik
|
||||||
#include <mapnik/expression.hpp>
|
#include <mapnik/expression.hpp>
|
||||||
|
#include <mapnik/text/char_properties_ptr.hpp>
|
||||||
|
|
||||||
// boost
|
// boost
|
||||||
#include <boost/property_tree/ptree_fwd.hpp>
|
#include <boost/property_tree/ptree_fwd.hpp>
|
||||||
|
|
||||||
namespace mapnik {
|
namespace mapnik {
|
||||||
|
|
||||||
|
class text_layout;
|
||||||
class feature_impl;
|
class feature_impl;
|
||||||
class processed_text;
|
|
||||||
class xml_node;
|
class xml_node;
|
||||||
struct char_properties;
|
|
||||||
|
|
||||||
namespace formatting {
|
namespace formatting {
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ public:
|
||||||
virtual ~node() {}
|
virtual ~node() {}
|
||||||
virtual void to_xml(boost::property_tree::ptree &xml) const;
|
virtual void to_xml(boost::property_tree::ptree &xml) const;
|
||||||
static node_ptr from_xml(xml_node const& xml);
|
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;
|
virtual void add_expressions(expression_set &output) const;
|
||||||
};
|
};
|
||||||
} //ns formatting
|
} //ns formatting
|
||||||
|
|
|
@ -32,7 +32,6 @@
|
||||||
namespace mapnik {
|
namespace mapnik {
|
||||||
|
|
||||||
class feature_impl;
|
class feature_impl;
|
||||||
class processed_text;
|
|
||||||
class xml_node;
|
class xml_node;
|
||||||
struct char_properties;
|
struct char_properties;
|
||||||
|
|
||||||
|
@ -41,7 +40,7 @@ class MAPNIK_DECL expression_format: public node {
|
||||||
public:
|
public:
|
||||||
void to_xml(boost::property_tree::ptree &xml) const;
|
void to_xml(boost::property_tree::ptree &xml) const;
|
||||||
static node_ptr from_xml(xml_node const& xml);
|
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;
|
virtual void add_expressions(expression_set &output) const;
|
||||||
|
|
||||||
void set_child(node_ptr child);
|
void set_child(node_ptr child);
|
||||||
|
@ -52,7 +51,6 @@ public:
|
||||||
expression_ptr character_spacing;
|
expression_ptr character_spacing;
|
||||||
expression_ptr line_spacing;
|
expression_ptr line_spacing;
|
||||||
expression_ptr text_opacity;
|
expression_ptr text_opacity;
|
||||||
expression_ptr wrap_before;
|
|
||||||
expression_ptr wrap_char;
|
expression_ptr wrap_char;
|
||||||
expression_ptr fill;
|
expression_ptr fill;
|
||||||
expression_ptr halo_fill;
|
expression_ptr halo_fill;
|
||||||
|
|
|
@ -36,7 +36,7 @@ class MAPNIK_DECL format_node: public node {
|
||||||
public:
|
public:
|
||||||
void to_xml(boost::property_tree::ptree &xml) const;
|
void to_xml(boost::property_tree::ptree &xml) const;
|
||||||
static node_ptr from_xml(xml_node const& xml);
|
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;
|
virtual void add_expressions(expression_set &output) const;
|
||||||
|
|
||||||
void set_child(node_ptr child);
|
void set_child(node_ptr child);
|
||||||
|
|
|
@ -35,7 +35,7 @@ class MAPNIK_DECL list_node: public node {
|
||||||
public:
|
public:
|
||||||
list_node() : node(), children_() {}
|
list_node() : node(), children_() {}
|
||||||
virtual void to_xml(boost::property_tree::ptree &xml) const;
|
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;
|
virtual void add_expressions(expression_set &output) const;
|
||||||
|
|
||||||
void push_back(node_ptr n);
|
void push_back(node_ptr n);
|
||||||
|
@ -49,4 +49,3 @@ protected:
|
||||||
} //ns mapnik
|
} //ns mapnik
|
||||||
|
|
||||||
#endif // FORMATTING_LIST_HPP
|
#endif // FORMATTING_LIST_HPP
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ public:
|
||||||
text_node(std::string text): node(), text_(parse_expression(text)) {}
|
text_node(std::string text): node(), text_(parse_expression(text)) {}
|
||||||
void to_xml(boost::property_tree::ptree &xml) const;
|
void to_xml(boost::property_tree::ptree &xml) const;
|
||||||
static node_ptr from_xml(xml_node const& xml);
|
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;
|
virtual void add_expressions(expression_set &output) const;
|
||||||
|
|
||||||
void set_text(expression_ptr text);
|
void set_text(expression_ptr text);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
*
|
*
|
||||||
* This file is part of Mapnik (c++ mapping toolkit)
|
* 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
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
@ -19,51 +19,50 @@
|
||||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*
|
*
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
#ifndef MAPNIK_GLYPH_INFO_HPP
|
||||||
|
#define MAPNIK_GLYPH_INFO_HPP
|
||||||
|
|
||||||
#ifndef MAPNIK_CHAR_INFO_HPP
|
//mapnik
|
||||||
#define MAPNIK_CHAR_INFO_HPP
|
#include <mapnik/text/char_properties_ptr.hpp>
|
||||||
|
#include <mapnik/pixel_position.hpp>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace mapnik {
|
namespace mapnik
|
||||||
struct char_properties;
|
|
||||||
|
|
||||||
class char_info
|
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
char_info(unsigned c_, double width_, double ymax_, double ymin_, double line_height_)
|
|
||||||
: c(c_),
|
|
||||||
width(width_),
|
|
||||||
line_height(line_height_),
|
|
||||||
ymin(ymin_),
|
|
||||||
ymax(ymax_),
|
|
||||||
avg_height(ymax - ymin),
|
|
||||||
format()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
char_info()
|
class font_face;
|
||||||
: c(0),
|
typedef std::shared_ptr<font_face> face_ptr;
|
||||||
width(0),
|
|
||||||
line_height(0),
|
|
||||||
ymin(0),
|
|
||||||
ymax(0),
|
|
||||||
avg_height(0),
|
|
||||||
format()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
double height() const { return ymax-ymin; }
|
typedef unsigned glyph_index_t;
|
||||||
|
|
||||||
unsigned c;
|
struct glyph_info
|
||||||
|
{
|
||||||
|
glyph_info()
|
||||||
|
: glyph_index(0),
|
||||||
|
face(nullptr),
|
||||||
|
char_index(0),
|
||||||
|
width(0.0),
|
||||||
|
ymin(0.0),
|
||||||
|
ymax(0.0),
|
||||||
|
line_height(0.0),
|
||||||
|
offset(),
|
||||||
|
format() {}
|
||||||
|
glyph_index_t glyph_index;
|
||||||
|
face_ptr face;
|
||||||
|
// Position in the string of all characters i.e. before itemizing
|
||||||
|
unsigned char_index;
|
||||||
double width;
|
double width;
|
||||||
double line_height;
|
|
||||||
double ymin;
|
double ymin;
|
||||||
double ymax;
|
double ymax;
|
||||||
double avg_height;
|
// Line height returned by freetype, includes normal font
|
||||||
char_properties *format;
|
// line spacing, but not additional user defined spacing
|
||||||
|
double line_height;
|
||||||
|
pixel_position offset;
|
||||||
|
char_properties_ptr format;
|
||||||
|
double height() const { return ymax-ymin; }
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
#endif //MAPNIK_CHAR_INFO_HPP
|
} //ns mapnik
|
||||||
|
|
||||||
|
#endif // GLYPH_INFO_HPP
|
123
include/mapnik/text/harfbuzz_shaper.hpp
Normal file
123
include/mapnik/text/harfbuzz_shaper.hpp
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef MAPNIK_HARFBUZZ_SHAPER_HPP
|
||||||
|
#define MAPNIK_HARFBUZZ_SHAPER_HPP
|
||||||
|
|
||||||
|
// mapnik
|
||||||
|
#include <mapnik/text/text_properties.hpp>
|
||||||
|
#include <mapnik/text/text_line.hpp>
|
||||||
|
#include <mapnik/text/face.hpp>
|
||||||
|
// stl
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
// harfbuzz
|
||||||
|
#include <harfbuzz/hb.h>
|
||||||
|
#include <harfbuzz/hb-ft.h>
|
||||||
|
#include <harfbuzz/hb-icu.h>
|
||||||
|
|
||||||
|
namespace mapnik
|
||||||
|
{
|
||||||
|
|
||||||
|
struct harfbuzz_shaper
|
||||||
|
{
|
||||||
|
static void shape_text(text_line & line,
|
||||||
|
text_itemizer & itemizer,
|
||||||
|
std::map<unsigned,double> & width_map,
|
||||||
|
face_manager_freetype & font_manager,
|
||||||
|
double scale_factor )
|
||||||
|
{
|
||||||
|
unsigned start = line.first_char();
|
||||||
|
unsigned end = line.last_char();
|
||||||
|
mapnik::value_unicode_string const& text = itemizer.text();
|
||||||
|
size_t length = end - start;
|
||||||
|
if (!length) return;
|
||||||
|
line.reserve(length);
|
||||||
|
std::list<text_item> const& list = itemizer.itemize(start, end);
|
||||||
|
|
||||||
|
auto hb_buffer_deleter = [](hb_buffer_t * buffer) { hb_buffer_destroy(buffer);};
|
||||||
|
const std::unique_ptr<hb_buffer_t, decltype(hb_buffer_deleter)> buffer(hb_buffer_create(),hb_buffer_deleter);
|
||||||
|
hb_buffer_set_unicode_funcs(buffer.get(), hb_icu_get_unicode_funcs());
|
||||||
|
hb_buffer_pre_allocate(buffer.get(), length);
|
||||||
|
const char* const shapers[] = { "ot", "fallback", NULL };
|
||||||
|
|
||||||
|
for (auto const& text_item : list)
|
||||||
|
{
|
||||||
|
face_set_ptr face_set = font_manager.get_face_set(text_item.format->face_name, text_item.format->fontset);
|
||||||
|
double size = text_item.format->text_size * scale_factor;
|
||||||
|
face_set->set_character_sizes(size);
|
||||||
|
font_face_set::iterator face_itr = face_set->begin(), face_end = face_set->end();
|
||||||
|
for (; face_itr != face_end; ++face_itr)
|
||||||
|
{
|
||||||
|
hb_buffer_clear_contents(buffer.get());
|
||||||
|
hb_buffer_add_utf16(buffer.get(), text.getBuffer(), text.length(), text_item.start, text_item.end - text_item.start);
|
||||||
|
hb_buffer_set_direction(buffer.get(), (text_item.rtl == UBIDI_RTL)?HB_DIRECTION_RTL:HB_DIRECTION_LTR);
|
||||||
|
hb_buffer_set_script(buffer.get(), hb_icu_script_to_script(text_item.script));
|
||||||
|
face_ptr const& face = *face_itr;
|
||||||
|
hb_font_t *font(hb_ft_font_create(face->get_face(), nullptr));
|
||||||
|
hb_shape_full(font, buffer.get(), NULL, 0, shapers);
|
||||||
|
hb_font_destroy(font);
|
||||||
|
|
||||||
|
unsigned num_glyphs = hb_buffer_get_length(buffer.get());
|
||||||
|
|
||||||
|
hb_glyph_info_t *glyphs = hb_buffer_get_glyph_infos(buffer.get(), nullptr);
|
||||||
|
hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(buffer.get(), nullptr);
|
||||||
|
|
||||||
|
bool font_has_all_glyphs = true;
|
||||||
|
// Check if all glyphs are valid.
|
||||||
|
for (unsigned i=0; i<num_glyphs; ++i)
|
||||||
|
{
|
||||||
|
if (!glyphs[i].codepoint)
|
||||||
|
{
|
||||||
|
font_has_all_glyphs = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!font_has_all_glyphs && face_itr+1 != face_end)
|
||||||
|
{
|
||||||
|
//Try next font in fontset
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned i=0; i<num_glyphs; ++i)
|
||||||
|
{
|
||||||
|
glyph_info tmp;
|
||||||
|
tmp.char_index = glyphs[i].cluster;
|
||||||
|
tmp.glyph_index = glyphs[i].codepoint;
|
||||||
|
tmp.face = face;
|
||||||
|
tmp.format = text_item.format;
|
||||||
|
face->glyph_dimensions(tmp);
|
||||||
|
//tmp.width = positions[i].x_advance / 64.0; //Overwrite default width with better value provided by HarfBuzz
|
||||||
|
tmp.width = positions[i].x_advance >> 6;
|
||||||
|
tmp.offset.set(positions[i].x_offset / 64.0, positions[i].y_offset / 64.0);
|
||||||
|
width_map[glyphs[i].cluster] += tmp.width;
|
||||||
|
line.add_glyph(tmp, scale_factor);
|
||||||
|
}
|
||||||
|
line.update_max_char_height(face->get_char_height());
|
||||||
|
break; //When we reach this point the current font had all glyphs.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace mapnik
|
||||||
|
|
||||||
|
#endif // MAPNIK_HARFBUZZ_SHAPER_HPP
|
123
include/mapnik/text/icu_shaper.hpp
Normal file
123
include/mapnik/text/icu_shaper.hpp
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef MAPNIK_ICU_SHAPER_HPP
|
||||||
|
#define MAPNIK_ICU_SHAPER_HPP
|
||||||
|
|
||||||
|
// mapnik
|
||||||
|
#include <mapnik/text/text_properties.hpp>
|
||||||
|
#include <mapnik/text/text_line.hpp>
|
||||||
|
#include <mapnik/text/face.hpp>
|
||||||
|
#include <mapnik/debug.hpp>
|
||||||
|
|
||||||
|
// stl
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
// icu
|
||||||
|
#include <unicode/unistr.h>
|
||||||
|
#include <unicode/ushape.h>
|
||||||
|
#include <unicode/schriter.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace mapnik
|
||||||
|
{
|
||||||
|
|
||||||
|
struct icu_shaper
|
||||||
|
{
|
||||||
|
static void shape_text(text_line & line,
|
||||||
|
text_itemizer & itemizer,
|
||||||
|
std::map<unsigned,double> & width_map,
|
||||||
|
face_manager_freetype & font_manager,
|
||||||
|
double scale_factor )
|
||||||
|
{
|
||||||
|
unsigned start = line.first_char();
|
||||||
|
unsigned end = line.last_char();
|
||||||
|
mapnik::value_unicode_string const& text = itemizer.text();
|
||||||
|
size_t length = end - start;
|
||||||
|
if (!length) return;
|
||||||
|
line.reserve(length);
|
||||||
|
std::list<text_item> const& list = itemizer.itemize(start, end);
|
||||||
|
|
||||||
|
UErrorCode err = U_ZERO_ERROR;
|
||||||
|
mapnik::value_unicode_string shaped;
|
||||||
|
mapnik::value_unicode_string reordered;
|
||||||
|
|
||||||
|
for (auto const& text_item : list)
|
||||||
|
{
|
||||||
|
face_set_ptr face_set = font_manager.get_face_set(text_item.format->face_name, text_item.format->fontset);
|
||||||
|
double size = text_item.format->text_size * scale_factor;
|
||||||
|
face_set->set_character_sizes(size);
|
||||||
|
for (auto const& face : *face_set)
|
||||||
|
{
|
||||||
|
UBiDi *bidi = ubidi_openSized(length, 0, &err);
|
||||||
|
ubidi_setPara(bidi, text.getBuffer(), length, UBIDI_DEFAULT_LTR, 0, &err);
|
||||||
|
ubidi_writeReordered(bidi, reordered.getBuffer(length),
|
||||||
|
length, UBIDI_DO_MIRRORING, &err);
|
||||||
|
ubidi_close(bidi);
|
||||||
|
reordered.releaseBuffer(length);
|
||||||
|
|
||||||
|
int32_t num_char = 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);
|
||||||
|
if (num_char < 0)
|
||||||
|
{
|
||||||
|
MAPNIK_LOG_ERROR(icu_shaper) << " u_shapeArabic returned negative num_char " << num_char;
|
||||||
|
}
|
||||||
|
std::size_t num_chars = static_cast<std::size_t>(num_char);
|
||||||
|
shaped.releaseBuffer(length);
|
||||||
|
bool shaped_status = true;
|
||||||
|
if (U_SUCCESS(err) && (num_chars == length))
|
||||||
|
{
|
||||||
|
U_NAMESPACE_QUALIFIER StringCharacterIterator iter(shaped);
|
||||||
|
unsigned i = 0;
|
||||||
|
for (iter.setToStart(); iter.hasNext();)
|
||||||
|
{
|
||||||
|
UChar ch = iter.nextPostInc();
|
||||||
|
glyph_info tmp;
|
||||||
|
tmp.offset.clear();
|
||||||
|
tmp.char_index = i;
|
||||||
|
tmp.glyph_index = FT_Get_Char_Index(face->get_face(), ch);
|
||||||
|
if (tmp.glyph_index == 0)
|
||||||
|
{
|
||||||
|
shaped_status = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tmp.face = face;
|
||||||
|
tmp.format = text_item.format;
|
||||||
|
face->glyph_dimensions(tmp);
|
||||||
|
width_map[i] += tmp.width;
|
||||||
|
line.add_glyph(tmp, scale_factor);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!shaped_status) continue;
|
||||||
|
line.update_max_char_height(face->get_char_height());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
} //namespace mapnik
|
||||||
|
|
||||||
|
#endif // MAPNIK_ICU_SHAPER_HPP
|
109
include/mapnik/text/itemizer.hpp
Normal file
109
include/mapnik/text/itemizer.hpp
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef MAPNIK_TEXT_ITEMIZER_HPP
|
||||||
|
#define MAPNIK_TEXT_ITEMIZER_HPP
|
||||||
|
|
||||||
|
//mapnik
|
||||||
|
#include <mapnik/text/char_properties_ptr.hpp>
|
||||||
|
#include <mapnik/value_types.hpp>
|
||||||
|
|
||||||
|
// stl
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// ICU
|
||||||
|
#include <unicode/unistr.h>
|
||||||
|
#include <unicode/uscript.h>
|
||||||
|
#include <unicode/ubidi.h>
|
||||||
|
namespace mapnik
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
struct text_item
|
||||||
|
{
|
||||||
|
/** First char (UTF16 offset) */
|
||||||
|
unsigned start;
|
||||||
|
/** Char _after_ the last char (UTF16 offset) */
|
||||||
|
unsigned end;
|
||||||
|
UScriptCode script;
|
||||||
|
char_properties_ptr format;
|
||||||
|
UBiDiDirection rtl;
|
||||||
|
text_item() :
|
||||||
|
start(0), end(0), script(), format(), rtl(UBIDI_LTR)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** This class splits text into parts which all have the same
|
||||||
|
* - direction (LTR, RTL)
|
||||||
|
* - format
|
||||||
|
* - script (http://en.wikipedia.org/wiki/Scripts_in_Unicode)
|
||||||
|
**/
|
||||||
|
class text_itemizer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
text_itemizer();
|
||||||
|
void add_text(mapnik::value_unicode_string str, char_properties_ptr format);
|
||||||
|
std::list<text_item> const& itemize(unsigned start=0, unsigned end=0);
|
||||||
|
void clear();
|
||||||
|
mapnik::value_unicode_string const& text() const { return text_; }
|
||||||
|
/** Returns the start and end position of a certain line.
|
||||||
|
*
|
||||||
|
* Only forced line breaks with \n characters are handled here.
|
||||||
|
*/
|
||||||
|
std::pair<unsigned, unsigned> line(unsigned i) const;
|
||||||
|
unsigned num_lines() const;
|
||||||
|
private:
|
||||||
|
template<typename T> struct run
|
||||||
|
{
|
||||||
|
run(T data, unsigned start, unsigned end) : start(start), end(end), data(data){}
|
||||||
|
unsigned start;
|
||||||
|
unsigned end;
|
||||||
|
T data;
|
||||||
|
};
|
||||||
|
typedef run<char_properties_ptr> format_run_t;
|
||||||
|
typedef run<UBiDiDirection> direction_run_t;
|
||||||
|
typedef run<UScriptCode> script_run_t;
|
||||||
|
typedef std::list<format_run_t> format_run_list;
|
||||||
|
typedef std::list<script_run_t> script_run_list;
|
||||||
|
typedef std::list<direction_run_t> direction_run_list;
|
||||||
|
mapnik::value_unicode_string text_;
|
||||||
|
/// Format runs are always sorted by char index
|
||||||
|
format_run_list format_runs_;
|
||||||
|
/// Directions runs are always in visual order! This is different from
|
||||||
|
/// format and script runs!
|
||||||
|
direction_run_list direction_runs_;
|
||||||
|
/// Script runs are always sorted by char index
|
||||||
|
script_run_list script_runs_;
|
||||||
|
void itemize_direction(unsigned start, unsigned end);
|
||||||
|
void itemize_script();
|
||||||
|
void create_item_list();
|
||||||
|
std::list<text_item> output_;
|
||||||
|
template <typename T> typename T::const_iterator find_run(T const& list, unsigned position);
|
||||||
|
std::vector<unsigned> forced_line_breaks_; //Positions of \n characters
|
||||||
|
};
|
||||||
|
} //ns mapnik
|
||||||
|
|
||||||
|
#endif // TEXT_ITEMIZER_HPP
|
109
include/mapnik/text/layout.hpp
Normal file
109
include/mapnik/text/layout.hpp
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
#ifndef MAPNIK_TEXT_LAYOUT_HPP
|
||||||
|
#define MAPNIK_TEXT_LAYOUT_HPP
|
||||||
|
|
||||||
|
//mapnik
|
||||||
|
#include <mapnik/text/itemizer.hpp>
|
||||||
|
#include <mapnik/font_engine_freetype.hpp>
|
||||||
|
#include <mapnik/text/glyph_info.hpp>
|
||||||
|
#include <mapnik/text/char_properties_ptr.hpp>
|
||||||
|
#include <mapnik/text/harfbuzz_shaper.hpp>
|
||||||
|
#include <mapnik/text/icu_shaper.hpp>
|
||||||
|
#include <mapnik/text/dummy_shaper.hpp>
|
||||||
|
|
||||||
|
//stl
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace mapnik
|
||||||
|
{
|
||||||
|
|
||||||
|
class text_layout
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef std::vector<text_line> line_vector;
|
||||||
|
typedef line_vector::const_iterator const_iterator;
|
||||||
|
typedef harfbuzz_shaper shaper_type;
|
||||||
|
text_layout(face_manager_freetype & font_manager, double scale_factor);
|
||||||
|
|
||||||
|
/** Adds a new text part. Call this function repeatedly to build the complete text. */
|
||||||
|
void add_text(mapnik::value_unicode_string const& str, char_properties_ptr format);
|
||||||
|
|
||||||
|
/** Returns the complete text stored in this layout.*/
|
||||||
|
mapnik::value_unicode_string const& text() const;
|
||||||
|
|
||||||
|
/** Processes the text into a list of glyphs, performing RTL/LTR handling, shaping and line breaking. */
|
||||||
|
void layout(double wrap_width, unsigned text_ratio, bool wrap_before);
|
||||||
|
|
||||||
|
/** Clear all data stored in this object. The object's state is the same as directly after construction. */
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
// Height of all lines together (in pixels).
|
||||||
|
inline double height() const { return height_; }
|
||||||
|
// Width of the longest line (in pixels).
|
||||||
|
inline double width() const { return width_ ; }
|
||||||
|
|
||||||
|
// Line iterator.
|
||||||
|
inline const_iterator begin() const { return lines_.begin(); }
|
||||||
|
inline const_iterator end() const { return lines_.end(); }
|
||||||
|
|
||||||
|
// Number of lines.
|
||||||
|
inline std::size_t num_lines() const { return lines_.size(); }
|
||||||
|
|
||||||
|
// Width of a certain glyph cluster (in pixels).
|
||||||
|
inline double cluster_width(unsigned cluster) const
|
||||||
|
{
|
||||||
|
std::map<unsigned, double>::const_iterator width_itr = width_map_.find(cluster);
|
||||||
|
if (width_itr != width_map_.end()) return width_itr->second;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the number of glyphs so memory can be preallocated.
|
||||||
|
inline unsigned glyphs_count() const { return glyphs_count_;}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void break_line(text_line & line, double wrap_width, unsigned text_ratio, bool wrap_before);
|
||||||
|
void shape_text(text_line & line);
|
||||||
|
void add_line(text_line & line);
|
||||||
|
void clear_cluster_widths(unsigned first, unsigned last);
|
||||||
|
|
||||||
|
//input
|
||||||
|
face_manager_freetype &font_manager_;
|
||||||
|
double scale_factor_;
|
||||||
|
|
||||||
|
//processing
|
||||||
|
text_itemizer itemizer_;
|
||||||
|
// Maps char index (UTF-16) to width. If multiple glyphs map to the same char the sum of all widths is used
|
||||||
|
// note: this probably isn't the best solution. it would be better to have an object for each cluster, but
|
||||||
|
// it needs to be implemented with no overhead.
|
||||||
|
std::map<unsigned, double> width_map_;
|
||||||
|
double width_;
|
||||||
|
double height_;
|
||||||
|
unsigned glyphs_count_;
|
||||||
|
|
||||||
|
//output
|
||||||
|
line_vector lines_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // TEXT_LAYOUT_HPP
|
|
@ -2,7 +2,7 @@
|
||||||
*
|
*
|
||||||
* This file is part of Mapnik (c++ mapping toolkit)
|
* 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
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* 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
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*
|
*
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
#ifndef MAPNIK_PLACEMENT_FINDER_HPP
|
#ifndef MAPNIK_PLACEMENT_FINDER_HPP
|
||||||
#define MAPNIK_PLACEMENT_FINDER_HPP
|
#define MAPNIK_PLACEMENT_FINDER_HPP
|
||||||
|
|
||||||
// mapnik
|
//mapnik
|
||||||
#include <mapnik/geometry.hpp>
|
#include <mapnik/box2d.hpp>
|
||||||
#include <mapnik/feature.hpp>
|
#include <mapnik/pixel_position.hpp>
|
||||||
#include <mapnik/text/text_properties.hpp>
|
#include <mapnik/text/layout.hpp>
|
||||||
#include <mapnik/text/text_path.hpp>
|
#include <mapnik/text/placements/base.hpp>
|
||||||
#include <mapnik/label_collision_detector.hpp>
|
#include <mapnik/text/placements_list.hpp>
|
||||||
#include <mapnik/ctrans.hpp>
|
#include <mapnik/text/rotation.hpp>
|
||||||
#include <mapnik/noncopyable.hpp>
|
#include <mapnik/noncopyable.hpp>
|
||||||
|
|
||||||
// agg
|
|
||||||
#include "agg_conv_clip_polyline.h"
|
|
||||||
|
|
||||||
// stl
|
|
||||||
#include <queue>
|
|
||||||
|
|
||||||
namespace mapnik
|
namespace mapnik
|
||||||
{
|
{
|
||||||
|
|
||||||
class text_placement_info;
|
class label_collision_detector4;
|
||||||
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;
|
|
||||||
|
|
||||||
typedef label_collision_detector4 DetectorType;
|
typedef label_collision_detector4 DetectorType;
|
||||||
|
|
||||||
|
class feature_impl;
|
||||||
|
class vertex_cache;
|
||||||
|
|
||||||
template <typename DetectorT>
|
|
||||||
class placement_finder : mapnik::noncopyable
|
class placement_finder : mapnik::noncopyable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
placement_finder(text_placement_info const& placement_info,
|
placement_finder(feature_impl const& feature,
|
||||||
string_info const& info,
|
DetectorType & detector,
|
||||||
DetectorT & detector,
|
box2d<double> const& extent,
|
||||||
box2d<double> const& extent);
|
text_placement_info_ptr placement_info,
|
||||||
|
face_manager_freetype & font_manager,
|
||||||
|
double scale_factor);
|
||||||
|
|
||||||
// Try place a single label at the given point. */
|
/** Try to place a single label at the given point. */
|
||||||
void find_point_placement(double pos_x, double pos_y, double angle=0.0);
|
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. */
|
||||||
// Iterate over the given path, placing point labels with respect to label_spacing. */
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void find_point_placements(T & path);
|
bool find_line_placements(T & path, bool points);
|
||||||
|
/** Try next position alternative from placement_info. */
|
||||||
|
bool next_position();
|
||||||
|
|
||||||
// Iterate over the given path, placing line-following labels with respect to label_spacing. */
|
placements_list const& placements() const { return placements_; }
|
||||||
template <typename T>
|
|
||||||
void find_line_placements(T & path);
|
|
||||||
|
|
||||||
// Add placements to detector. */
|
|
||||||
void update_detector();
|
|
||||||
|
|
||||||
// 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_; }
|
|
||||||
|
|
||||||
|
void set_marker(marker_info_ptr m, box2d<double> box, bool marker_unlocked, pixel_position const& marker_displacement);
|
||||||
private:
|
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 init_alignment();
|
||||||
void adjust_position(text_path *current_placement);
|
pixel_position alignment_offset() const;
|
||||||
void add_line(double width, double height, bool first_line);
|
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_;
|
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_;
|
justify_alignment_e jalign_;
|
||||||
std::vector<std::size_t> line_breaks_;
|
double scale_factor_;
|
||||||
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_;
|
|
||||||
|
|
||||||
// Additional boxes to take into account when finding placement.
|
placements_list placements_;
|
||||||
// Used for finding line placements where multiple placements are returned.
|
|
||||||
// Boxes are relative to starting point of current placement.
|
//ShieldSymbolizer
|
||||||
// Only used for point placements!
|
bool has_marker_;
|
||||||
std::vector<box2d<double> > additional_boxes_;
|
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
|
||||||
|
|
|
@ -33,7 +33,7 @@ namespace mapnik
|
||||||
typedef std::pair<double,double> dimension_type;
|
typedef std::pair<double,double> dimension_type;
|
||||||
|
|
||||||
class MAPNIK_DECL text_placements;
|
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
|
* This placement has first to be tested by placement_finder to verify it
|
||||||
* can actually be used.
|
* can actually be used.
|
||||||
*/
|
*/
|
||||||
|
@ -58,16 +58,7 @@ public:
|
||||||
|
|
||||||
/** Scale factor used by the renderer. */
|
/** Scale factor used by the renderer. */
|
||||||
double scale_factor;
|
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;
|
typedef std::shared_ptr<text_placement_info> text_placement_info_ptr;
|
||||||
|
|
95
include/mapnik/text/placements_list.hpp
Normal file
95
include/mapnik/text/placements_list.hpp
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
#ifndef MAPNIK_PLACEMENTS_LIST_HPP
|
||||||
|
#define MAPNIK_PLACEMENTS_LIST_HPP
|
||||||
|
//mapnik
|
||||||
|
#include <mapnik/box2d.hpp>
|
||||||
|
#include <mapnik/pixel_position.hpp>
|
||||||
|
#include <mapnik/text/glyph_info.hpp>
|
||||||
|
#include <mapnik/text/rotation.hpp>
|
||||||
|
#include <mapnik/marker_cache.hpp>
|
||||||
|
|
||||||
|
// agg
|
||||||
|
#include "agg_trans_affine.h"
|
||||||
|
|
||||||
|
//stl
|
||||||
|
#include <vector>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
namespace mapnik
|
||||||
|
{
|
||||||
|
|
||||||
|
struct glyph_info;
|
||||||
|
|
||||||
|
struct glyph_position
|
||||||
|
{
|
||||||
|
glyph_position(glyph_info const& glyph, pixel_position const& pos, rotation const& rot)
|
||||||
|
: glyph(&glyph), pos(pos), rot(rot) { }
|
||||||
|
glyph_info const* glyph;
|
||||||
|
pixel_position pos;
|
||||||
|
rotation rot;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct marker_info
|
||||||
|
{
|
||||||
|
marker_info() : marker(), transform() {}
|
||||||
|
marker_info(marker_ptr marker, agg::trans_affine const& transform) :
|
||||||
|
marker(marker), transform(transform) {}
|
||||||
|
marker_ptr marker;
|
||||||
|
agg::trans_affine transform;
|
||||||
|
};
|
||||||
|
typedef std::shared_ptr<marker_info> marker_info_ptr;
|
||||||
|
|
||||||
|
/** Stores positions of glphys.
|
||||||
|
*
|
||||||
|
* The actual glyphs and their format are stored in text_layout.
|
||||||
|
*/
|
||||||
|
class glyph_positions
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef std::vector<glyph_position>::const_iterator const_iterator;
|
||||||
|
glyph_positions();
|
||||||
|
|
||||||
|
const_iterator begin() const;
|
||||||
|
const_iterator end() const;
|
||||||
|
|
||||||
|
void push_back(glyph_info const& glyph, pixel_position offset, rotation const& rot);
|
||||||
|
void reserve(unsigned count);
|
||||||
|
|
||||||
|
pixel_position const& get_base_point() const;
|
||||||
|
void set_base_point(pixel_position base_point);
|
||||||
|
void set_marker(marker_info_ptr marker, pixel_position const& marker_pos);
|
||||||
|
marker_info_ptr marker() const;
|
||||||
|
pixel_position const& marker_pos() const;
|
||||||
|
box2d<double> const & bbox() const;
|
||||||
|
private:
|
||||||
|
std::vector<glyph_position> data_;
|
||||||
|
pixel_position base_point_;
|
||||||
|
marker_info_ptr marker_;
|
||||||
|
pixel_position marker_pos_;
|
||||||
|
box2d<double> bbox_;
|
||||||
|
};
|
||||||
|
typedef std::shared_ptr<glyph_positions> glyph_positions_ptr;
|
||||||
|
|
||||||
|
typedef std::list<glyph_positions_ptr> placements_list;
|
||||||
|
}
|
||||||
|
#endif // PLACEMENTS_LIST_HPP
|
|
@ -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
|
|
116
include/mapnik/text/renderer.hpp
Normal file
116
include/mapnik/text/renderer.hpp
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef MAPNIK_TEXT_RENDERER_HPP
|
||||||
|
#define MAPNIK_TEXT_RENDERER_HPP
|
||||||
|
|
||||||
|
//mapnik
|
||||||
|
#include <mapnik/text/placement_finder.hpp>
|
||||||
|
#include <mapnik/image_compositing.hpp>
|
||||||
|
#include <mapnik/text_symbolizer.hpp>
|
||||||
|
#include <mapnik/noncopyable.hpp>
|
||||||
|
|
||||||
|
//TODO: Find a better place for halo_rasterizer_e!
|
||||||
|
//TODO: Halo rasterizer selection should go to text_properties because it might make sense to use a different rasterizer for different fonts
|
||||||
|
|
||||||
|
// freetype2
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#include <ft2build.h>
|
||||||
|
#include FT_FREETYPE_H
|
||||||
|
#include FT_STROKER_H
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace mapnik
|
||||||
|
{
|
||||||
|
|
||||||
|
struct glyph_t
|
||||||
|
{
|
||||||
|
FT_Glyph image;
|
||||||
|
char_properties_ptr properties;
|
||||||
|
|
||||||
|
glyph_t(FT_Glyph image_, char_properties_ptr properties_)
|
||||||
|
: image(image_), properties(properties_) {}
|
||||||
|
|
||||||
|
glyph_t( glyph_t && other) noexcept
|
||||||
|
: image(other.image),
|
||||||
|
properties(std::move(other.properties))
|
||||||
|
{
|
||||||
|
other.image = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
glyph_t(glyph_t const& ) = delete;
|
||||||
|
glyph_t & operator=(glyph_t const&) = delete;
|
||||||
|
|
||||||
|
~glyph_t () { FT_Done_Glyph(image);}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class text_renderer : private mapnik::noncopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
text_renderer (halo_rasterizer_e rasterizer,
|
||||||
|
composite_mode_e comp_op = src_over,
|
||||||
|
double scale_factor=1.0,
|
||||||
|
stroker_ptr stroker=stroker_ptr());
|
||||||
|
protected:
|
||||||
|
typedef std::vector<glyph_t> glyph_vector;
|
||||||
|
void prepare_glyphs(glyph_positions const& positions);
|
||||||
|
halo_rasterizer_e rasterizer_;
|
||||||
|
composite_mode_e comp_op_;
|
||||||
|
double scale_factor_;
|
||||||
|
glyph_vector glyphs_;
|
||||||
|
stroker_ptr stroker_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class agg_text_renderer : public text_renderer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef T pixmap_type;
|
||||||
|
agg_text_renderer (pixmap_type & pixmap, halo_rasterizer_e rasterizer,
|
||||||
|
composite_mode_e comp_op = src_over,
|
||||||
|
double scale_factor=1.0,
|
||||||
|
stroker_ptr stroker=stroker_ptr());
|
||||||
|
void render(glyph_positions const& positions);
|
||||||
|
private:
|
||||||
|
pixmap_type & pixmap_;
|
||||||
|
void render_halo(FT_Bitmap_ *bitmap, unsigned rgba, int x, int y,
|
||||||
|
double halo_radius, double opacity,
|
||||||
|
composite_mode_e comp_op);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class grid_text_renderer : public text_renderer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef T pixmap_type;
|
||||||
|
grid_text_renderer (pixmap_type & pixmap, composite_mode_e comp_op = src_over,
|
||||||
|
double scale_factor=1.0);
|
||||||
|
void render(glyph_positions const& positions, value_integer feature_id);
|
||||||
|
private:
|
||||||
|
pixmap_type & pixmap_;
|
||||||
|
void render_halo_id(FT_Bitmap_ *bitmap, mapnik::value_integer feature_id, int x, int y, int halo_radius);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif // RENDERER_HPP
|
23
include/mapnik/text/rotation.hpp
Normal file
23
include/mapnik/text/rotation.hpp
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef MAPNIK_ROTATION_HPP
|
||||||
|
#define MAPNIK_ROTATION_HPP
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace mapnik
|
||||||
|
{
|
||||||
|
|
||||||
|
struct rotation
|
||||||
|
{
|
||||||
|
rotation() : sin(0), cos(1.) { }
|
||||||
|
rotation(double sin_, double cos_) : sin(sin_), cos(cos_) { }
|
||||||
|
rotation(double angle) : sin(std::sin(angle)), cos(std::cos(angle)) {}
|
||||||
|
void reset() { sin = 0.; cos = 1.;}
|
||||||
|
void init(double angle) { sin = std::sin(angle); cos = std::cos(angle); }
|
||||||
|
double sin;
|
||||||
|
double cos;
|
||||||
|
rotation operator~() { return rotation(sin, -cos); }
|
||||||
|
rotation operator!() { return rotation(-sin, cos); }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ROTATION_HPP
|
157
include/mapnik/text/scrptrun.hpp
Normal file
157
include/mapnik/text/scrptrun.hpp
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
/*
|
||||||
|
*******************************************************************************
|
||||||
|
*
|
||||||
|
* Copyright (C) 1999-2003, International Business Machines
|
||||||
|
* Corporation and others. All Rights Reserved.
|
||||||
|
*
|
||||||
|
*******************************************************************************
|
||||||
|
* file name: scrptrun.h
|
||||||
|
*
|
||||||
|
* created on: 10/17/2001
|
||||||
|
* created by: Eric R. Mader
|
||||||
|
*
|
||||||
|
* NOTE: This file is copied from ICU.
|
||||||
|
* http://source.icu-project.org/repos/icu/icu/trunk/license.html
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SCRPTRUN_H
|
||||||
|
#define __SCRPTRUN_H
|
||||||
|
|
||||||
|
#include "unicode/utypes.h"
|
||||||
|
#include "unicode/uobject.h"
|
||||||
|
#include "unicode/uscript.h"
|
||||||
|
|
||||||
|
struct ScriptRecord
|
||||||
|
{
|
||||||
|
UChar32 startChar;
|
||||||
|
UChar32 endChar;
|
||||||
|
UScriptCode scriptCode;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ParenStackEntry
|
||||||
|
{
|
||||||
|
int32_t pairIndex;
|
||||||
|
UScriptCode scriptCode;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ScriptRun : public UObject {
|
||||||
|
public:
|
||||||
|
ScriptRun();
|
||||||
|
|
||||||
|
ScriptRun(const UChar chars[], int32_t length);
|
||||||
|
|
||||||
|
ScriptRun(const UChar chars[], int32_t start, int32_t length);
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
void reset(int32_t start, int32_t count);
|
||||||
|
|
||||||
|
void reset(const UChar chars[], int32_t start, int32_t length);
|
||||||
|
|
||||||
|
int32_t getScriptStart();
|
||||||
|
|
||||||
|
int32_t getScriptEnd();
|
||||||
|
|
||||||
|
UScriptCode getScriptCode();
|
||||||
|
|
||||||
|
UBool next();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ICU "poor man's RTTI", returns a UClassID for the actual class.
|
||||||
|
*
|
||||||
|
* @stable ICU 2.2
|
||||||
|
*/
|
||||||
|
virtual inline UClassID getDynamicClassID() const { return getStaticClassID(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ICU "poor man's RTTI", returns a UClassID for this class.
|
||||||
|
*
|
||||||
|
* @stable ICU 2.2
|
||||||
|
*/
|
||||||
|
static inline UClassID getStaticClassID() { return (UClassID)&fgClassID; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
static UBool sameScript(int32_t scriptOne, int32_t scriptTwo);
|
||||||
|
|
||||||
|
int32_t charStart;
|
||||||
|
int32_t charLimit;
|
||||||
|
const UChar *charArray;
|
||||||
|
|
||||||
|
int32_t scriptStart;
|
||||||
|
int32_t scriptEnd;
|
||||||
|
UScriptCode scriptCode;
|
||||||
|
|
||||||
|
ParenStackEntry parenStack[128];
|
||||||
|
int32_t parenSP;
|
||||||
|
|
||||||
|
static int8_t highBit(int32_t value);
|
||||||
|
static int32_t getPairIndex(UChar32 ch);
|
||||||
|
|
||||||
|
static UChar32 pairedChars[];
|
||||||
|
static const int32_t pairedCharCount;
|
||||||
|
static const int32_t pairedCharPower;
|
||||||
|
static const int32_t pairedCharExtra;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The address of this static class variable serves as this class's ID
|
||||||
|
* for ICU "poor man's RTTI".
|
||||||
|
*/
|
||||||
|
static const char fgClassID;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline ScriptRun::ScriptRun()
|
||||||
|
{
|
||||||
|
reset(NULL, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ScriptRun::ScriptRun(const UChar chars[], int32_t length)
|
||||||
|
{
|
||||||
|
reset(chars, 0, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ScriptRun::ScriptRun(const UChar chars[], int32_t start, int32_t length)
|
||||||
|
{
|
||||||
|
reset(chars, start, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int32_t ScriptRun::getScriptStart()
|
||||||
|
{
|
||||||
|
return scriptStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int32_t ScriptRun::getScriptEnd()
|
||||||
|
{
|
||||||
|
return scriptEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline UScriptCode ScriptRun::getScriptCode()
|
||||||
|
{
|
||||||
|
return scriptCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ScriptRun::reset()
|
||||||
|
{
|
||||||
|
scriptStart = charStart;
|
||||||
|
scriptEnd = charStart;
|
||||||
|
scriptCode = USCRIPT_INVALID_CODE;
|
||||||
|
parenSP = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ScriptRun::reset(int32_t start, int32_t length)
|
||||||
|
{
|
||||||
|
charStart = start;
|
||||||
|
charLimit = start + length;
|
||||||
|
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ScriptRun::reset(const UChar chars[], int32_t start, int32_t length)
|
||||||
|
{
|
||||||
|
charArray = chars;
|
||||||
|
|
||||||
|
reset(start, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -2,7 +2,7 @@
|
||||||
*
|
*
|
||||||
* This file is part of Mapnik (c++ mapping toolkit)
|
* 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
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
@ -26,34 +26,21 @@
|
||||||
#include <mapnik/text_symbolizer.hpp>
|
#include <mapnik/text_symbolizer.hpp>
|
||||||
#include <mapnik/shield_symbolizer.hpp>
|
#include <mapnik/shield_symbolizer.hpp>
|
||||||
#include <mapnik/feature.hpp>
|
#include <mapnik/feature.hpp>
|
||||||
|
#include <mapnik/marker.hpp>
|
||||||
#include <mapnik/marker_cache.hpp>
|
#include <mapnik/marker_cache.hpp>
|
||||||
#include <mapnik/text/processed_text.hpp>
|
#include <mapnik/text/placement_finder.hpp>
|
||||||
#include <mapnik/text/text_path.hpp>
|
#include <mapnik/proj_transform.hpp>
|
||||||
|
#include <mapnik/ctrans.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace mapnik {
|
namespace mapnik {
|
||||||
|
|
||||||
/** Helper object that does all the TextSymbolizer placement finding
|
/** Helper object that does all the TextSymbolizer placement finding
|
||||||
* work except actually rendering the object. */
|
* work except actually rendering the object. */
|
||||||
template <typename FaceManagerT, typename DetectorT>
|
|
||||||
class text_symbolizer_helper
|
class text_symbolizer_helper
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
template <typename FaceManagerT, typename DetectorT>
|
||||||
text_symbolizer_helper(text_symbolizer const& sym,
|
text_symbolizer_helper(text_symbolizer const& sym,
|
||||||
feature_impl const& feature,
|
feature_impl const& feature,
|
||||||
proj_transform const& prj_trans,
|
proj_transform const& prj_trans,
|
||||||
|
@ -65,18 +52,23 @@ public:
|
||||||
DetectorT &detector,
|
DetectorT &detector,
|
||||||
box2d<double> const& query_extent);
|
box2d<double> const& query_extent);
|
||||||
|
|
||||||
~text_symbolizer_helper();
|
template <typename FaceManagerT, typename DetectorT>
|
||||||
// Return next placement.
|
text_symbolizer_helper(shield_symbolizer const& sym,
|
||||||
// If no more placements are found returns null pointer.
|
feature_impl const& feature,
|
||||||
bool next();
|
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!
|
/** Return all placements.*/
|
||||||
placements_type const& placements() const;
|
placements_list const& get();
|
||||||
protected:
|
protected:
|
||||||
bool next_point_placement();
|
bool next_point_placement();
|
||||||
bool next_line_placement();
|
bool next_line_placement();
|
||||||
bool next_line_placement_clipped();
|
|
||||||
bool next_placement();
|
|
||||||
void initialize_geometries();
|
void initialize_geometries();
|
||||||
void initialize_points();
|
void initialize_points();
|
||||||
|
|
||||||
|
@ -85,103 +77,29 @@ protected:
|
||||||
feature_impl const& feature_;
|
feature_impl const& feature_;
|
||||||
proj_transform const& prj_trans_;
|
proj_transform const& prj_trans_;
|
||||||
CoordTransform const& t_;
|
CoordTransform const& t_;
|
||||||
FaceManagerT & font_manager_;
|
|
||||||
DetectorT & detector_;
|
|
||||||
box2d<double> dims_;
|
box2d<double> dims_;
|
||||||
box2d<double> const& query_extent_;
|
box2d<double> const& query_extent_;
|
||||||
//Processing
|
//Processing
|
||||||
processed_text text_;
|
/* Using list instead of vector, because we delete random elements and need iterators to stay valid. */
|
||||||
// Using list instead of vector, because we delete random elements and need iterators to stay valid.
|
/** Remaining geometries to be processed. */
|
||||||
// Remaining geometries to be processed.
|
|
||||||
std::list<geometry_type*> geometries_to_process_;
|
std::list<geometry_type*> geometries_to_process_;
|
||||||
// Geometry currently being processed.
|
/** Geometry currently being processed. */
|
||||||
std::list<geometry_type*>::iterator geo_itr_;
|
std::list<geometry_type*>::iterator geo_itr_;
|
||||||
// Remaining points to be processed.
|
/** Remaining points to be processed. */
|
||||||
std::list<position> points_;
|
std::list<pixel_position> points_;
|
||||||
// Point currently being processed.
|
/** Point currently being processed. */
|
||||||
std::list<position>::iterator point_itr_;
|
std::list<pixel_position>::iterator point_itr_;
|
||||||
// Text rotation.
|
/** Use point placement. Otherwise line placement is used. */
|
||||||
double angle_;
|
|
||||||
// Did last call to next_placement return true?
|
|
||||||
bool placement_valid_;
|
|
||||||
// Use point placement. Otherwise line placement is used.
|
|
||||||
bool point_placement_;
|
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_;
|
bool points_on_line_;
|
||||||
|
|
||||||
text_placement_info_ptr placement_;
|
text_placement_info_ptr placement_;
|
||||||
std::unique_ptr<placement_finder<DetectorT> > finder_;
|
placement_finder finder_;
|
||||||
};
|
|
||||||
|
|
||||||
template <typename FaceManagerT, typename DetectorT>
|
//ShieldSymbolizer only
|
||||||
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_;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
void init_marker();
|
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
|
} //namespace
|
||||||
#endif // SYMBOLIZER_HELPERS_HPP
|
#endif // SYMBOLIZER_HELPERS_HPP
|
||||||
|
|
93
include/mapnik/text/text_line.hpp
Normal file
93
include/mapnik/text/text_line.hpp
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
#ifndef MAPNIK_TEXT_LINE_HPP
|
||||||
|
#define MAPNIK_TEXT_LINE_HPP
|
||||||
|
|
||||||
|
//mapnik
|
||||||
|
#include <mapnik/text/glyph_info.hpp>
|
||||||
|
#include <mapnik/text/text_properties.hpp>
|
||||||
|
//stl
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace mapnik
|
||||||
|
{
|
||||||
|
|
||||||
|
/** This class stores all glyphs of a line in left to right order.
|
||||||
|
*
|
||||||
|
* It can be used for rendering but no text processing (like line breaking)
|
||||||
|
* should be done!
|
||||||
|
*/
|
||||||
|
class text_line
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
text_line(unsigned first_char, unsigned last_char);
|
||||||
|
|
||||||
|
typedef std::vector<glyph_info> glyph_vector;
|
||||||
|
typedef glyph_vector::const_iterator const_iterator;
|
||||||
|
/** Get glyph vector. */
|
||||||
|
glyph_vector const& glyphs() const { return glyphs_; }
|
||||||
|
/** Append glyph. */
|
||||||
|
void add_glyph(glyph_info const& glyph, double scale_factor_);
|
||||||
|
|
||||||
|
/** Preallocate memory. */
|
||||||
|
void reserve(glyph_vector::size_type length);
|
||||||
|
/** Iterator to first glyph. */
|
||||||
|
const_iterator begin() const;
|
||||||
|
/** Iterator beyond last glyph. */
|
||||||
|
const_iterator end() const;
|
||||||
|
|
||||||
|
/** Width of all glyphs including character spacing. */
|
||||||
|
double width() const { return width_; }
|
||||||
|
/** Real line height. For first line: max_char_height(), for all others: line_height(). */
|
||||||
|
double height() const;
|
||||||
|
|
||||||
|
/** Height of the tallest glyph in this line. */
|
||||||
|
double max_char_height() const { return max_char_height_; }
|
||||||
|
/** Called for each font/style to update the maximum height of this line. */
|
||||||
|
void update_max_char_height(double max_char_height);
|
||||||
|
/** Line height including line spacing. */
|
||||||
|
double line_height() const { return line_height_; }
|
||||||
|
|
||||||
|
/** Is this object is the first line of a multi-line text?
|
||||||
|
* Used to exclude linespacing from first line's height. */
|
||||||
|
void set_first_line(bool first_line);
|
||||||
|
|
||||||
|
/** Index of first UTF-16 char. */
|
||||||
|
unsigned first_char() const;
|
||||||
|
/** Index of last UTF-16 char. */
|
||||||
|
unsigned last_char() const;
|
||||||
|
|
||||||
|
/** Number of glyphs. */
|
||||||
|
unsigned size() const;
|
||||||
|
private:
|
||||||
|
glyph_vector glyphs_;
|
||||||
|
double line_height_; //Includes line spacing (returned by freetype)
|
||||||
|
double max_char_height_; //Height of 'X' character of the largest font in this run. //TODO: Initialize this!
|
||||||
|
double width_;
|
||||||
|
unsigned first_char_;
|
||||||
|
unsigned last_char_;
|
||||||
|
bool first_line_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} //namespace mapnik
|
||||||
|
|
||||||
|
#endif // MAPNIK_TEXT_LINE_HPP
|
|
@ -2,7 +2,7 @@
|
||||||
*
|
*
|
||||||
* This file is part of Mapnik (c++ mapping toolkit)
|
* 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
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
*
|
*
|
||||||
* This file is part of Mapnik (c++ mapping toolkit)
|
* 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
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
@ -23,6 +23,7 @@
|
||||||
#define TEXT_PROPERTIES_HPP
|
#define TEXT_PROPERTIES_HPP
|
||||||
|
|
||||||
// mapnik
|
// mapnik
|
||||||
|
#include <mapnik/text/char_properties_ptr.hpp>
|
||||||
#include <mapnik/color.hpp>
|
#include <mapnik/color.hpp>
|
||||||
#include <mapnik/attribute.hpp>
|
#include <mapnik/attribute.hpp>
|
||||||
#include <mapnik/value.hpp>
|
#include <mapnik/value.hpp>
|
||||||
|
@ -31,6 +32,7 @@
|
||||||
#include <mapnik/enumeration.hpp>
|
#include <mapnik/enumeration.hpp>
|
||||||
#include <mapnik/expression.hpp>
|
#include <mapnik/expression.hpp>
|
||||||
#include <mapnik/text/formatting/base.hpp>
|
#include <mapnik/text/formatting/base.hpp>
|
||||||
|
#include <mapnik/pixel_position.hpp>
|
||||||
|
|
||||||
// stl
|
// stl
|
||||||
#include <map>
|
#include <map>
|
||||||
|
@ -67,7 +69,6 @@ struct MAPNIK_DECL char_properties
|
||||||
double character_spacing;
|
double character_spacing;
|
||||||
double line_spacing; //Largest total height (fontsize+line_spacing) per line is chosen
|
double line_spacing; //Largest total height (fontsize+line_spacing) per line is chosen
|
||||||
double text_opacity;
|
double text_opacity;
|
||||||
bool wrap_before;
|
|
||||||
unsigned wrap_char;
|
unsigned wrap_char;
|
||||||
text_transform_e text_transform; //Per expression
|
text_transform_e text_transform; //Per expression
|
||||||
color fill;
|
color fill;
|
||||||
|
@ -75,7 +76,6 @@ struct MAPNIK_DECL char_properties
|
||||||
double halo_radius;
|
double halo_radius;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
enum label_placement_enum
|
enum label_placement_enum
|
||||||
{
|
{
|
||||||
POINT_PLACEMENT,
|
POINT_PLACEMENT,
|
||||||
|
@ -120,8 +120,19 @@ enum justify_alignment
|
||||||
|
|
||||||
DEFINE_ENUM(justify_alignment_e, justify_alignment);
|
DEFINE_ENUM(justify_alignment_e, justify_alignment);
|
||||||
|
|
||||||
typedef std::pair<double, double> position;
|
enum text_upright
|
||||||
class processed_text;
|
{
|
||||||
|
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. */
|
/** 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.
|
/** 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.
|
* 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. */
|
/** Automatically create processing instructions for a single expression. */
|
||||||
void set_old_style_expression(expression_ptr expr);
|
void set_old_style_expression(expression_ptr expr);
|
||||||
/** Sets new format tree. */
|
/** Sets new format tree. */
|
||||||
|
@ -149,7 +160,7 @@ struct MAPNIK_DECL text_symbolizer_properties
|
||||||
|
|
||||||
//Per symbolizer options
|
//Per symbolizer options
|
||||||
expression_ptr orientation;
|
expression_ptr orientation;
|
||||||
position displacement;
|
pixel_position displacement;
|
||||||
label_placement_e label_placement;
|
label_placement_e label_placement;
|
||||||
horizontal_alignment_e halign;
|
horizontal_alignment_e halign;
|
||||||
justify_alignment_e jalign;
|
justify_alignment_e jalign;
|
||||||
|
@ -170,8 +181,11 @@ struct MAPNIK_DECL text_symbolizer_properties
|
||||||
bool largest_bbox_only;
|
bool largest_bbox_only;
|
||||||
double text_ratio;
|
double text_ratio;
|
||||||
double wrap_width;
|
double wrap_width;
|
||||||
|
bool wrap_before;
|
||||||
|
bool rotate_displacement;
|
||||||
|
text_upright_e upright;
|
||||||
/** Default values for char_properties. */
|
/** Default values for char_properties. */
|
||||||
char_properties format;
|
char_properties_ptr format;
|
||||||
private:
|
private:
|
||||||
/** A tree of formatting::nodes which contain text and formatting information. */
|
/** A tree of formatting::nodes which contain text and formatting information. */
|
||||||
formatting::node_ptr tree_;
|
formatting::node_ptr tree_;
|
||||||
|
|
221
include/mapnik/text/vertex_cache.hpp
Normal file
221
include/mapnik/text/vertex_cache.hpp
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
#ifndef MAPNIK_VERTEX_CACHE_HPP
|
||||||
|
#define MAPNIK_VERTEX_CACHE_HPP
|
||||||
|
|
||||||
|
// mapnik
|
||||||
|
#include <mapnik/pixel_position.hpp>
|
||||||
|
#include <mapnik/debug.hpp>
|
||||||
|
|
||||||
|
// agg
|
||||||
|
#include "agg_basics.h"
|
||||||
|
|
||||||
|
// stl
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace mapnik
|
||||||
|
{
|
||||||
|
|
||||||
|
class vertex_cache;
|
||||||
|
typedef std::shared_ptr<vertex_cache> vertex_cache_ptr;
|
||||||
|
|
||||||
|
/** Caches all path points and their lengths. Allows easy moving in both directions. */
|
||||||
|
class vertex_cache
|
||||||
|
{
|
||||||
|
struct segment
|
||||||
|
{
|
||||||
|
segment(double x, double y, double length) : pos(x, y), length(length) {}
|
||||||
|
pixel_position pos; //Last point of this segment, first point is implicitly defined by the previous segement in this vector
|
||||||
|
double length;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The first segment always has the length 0 and just defines the starting point. */
|
||||||
|
struct segment_vector
|
||||||
|
{
|
||||||
|
segment_vector() : vector(), length(0.) {}
|
||||||
|
void add_segment(double x, double y, double len) {
|
||||||
|
if (len == 0. && !vector.empty()) return; //Don't add zero length segments
|
||||||
|
vector.push_back(segment(x, y, len));
|
||||||
|
length += len;
|
||||||
|
}
|
||||||
|
typedef std::vector<segment>::iterator iterator;
|
||||||
|
std::vector<segment> vector;
|
||||||
|
double length;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** This class has no public members to avoid acciedential modification.
|
||||||
|
* It should only be used with save_state/restore_state. */
|
||||||
|
class state
|
||||||
|
{
|
||||||
|
segment_vector::iterator current_segment;
|
||||||
|
double position_in_segment;
|
||||||
|
pixel_position current_position;
|
||||||
|
pixel_position segment_starting_point;
|
||||||
|
double position_;
|
||||||
|
friend class vertex_cache;
|
||||||
|
public:
|
||||||
|
pixel_position const& position() const { return current_position; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class scoped_state
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
scoped_state(vertex_cache &pp) : pp_(pp), state_(pp.save_state()), restored_(false) {}
|
||||||
|
void restore() { pp_.restore_state(state_); restored_ = true; }
|
||||||
|
~scoped_state() { if (!restored_) pp_.restore_state(state_); }
|
||||||
|
state const& get_state() const { return state_; }
|
||||||
|
private:
|
||||||
|
vertex_cache &pp_;
|
||||||
|
class state state_;
|
||||||
|
bool restored_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/********************************************************************************************/
|
||||||
|
|
||||||
|
template <typename T> vertex_cache(T &path);
|
||||||
|
|
||||||
|
double length() const { return current_subpath_->length; }
|
||||||
|
|
||||||
|
|
||||||
|
pixel_position const& current_position() const { return current_position_; }
|
||||||
|
double angle(double width=0.);
|
||||||
|
double linear_position() const { return position_; }
|
||||||
|
|
||||||
|
|
||||||
|
/** Returns a parallel line in the specified distance. */
|
||||||
|
vertex_cache &get_offseted(double offset, double region_width);
|
||||||
|
|
||||||
|
|
||||||
|
/** Skip a certain amount of space.
|
||||||
|
*
|
||||||
|
* This function automatically calculates new points if the position is not exactly
|
||||||
|
* on a point on the path.
|
||||||
|
*/
|
||||||
|
bool forward(double length);
|
||||||
|
/** Go backwards. */
|
||||||
|
bool backward(double length);
|
||||||
|
/** Move in any direction (based on sign of length). Returns false if it reaches either end of the path. */
|
||||||
|
bool move(double length);
|
||||||
|
/** Work on next subpath. Returns false if the is no next subpath. */
|
||||||
|
bool next_subpath();
|
||||||
|
|
||||||
|
// Compatibility with standard path interface
|
||||||
|
void rewind(unsigned);
|
||||||
|
unsigned vertex(double *x, double *y);
|
||||||
|
|
||||||
|
//State
|
||||||
|
state save_state() const;
|
||||||
|
void restore_state(state const& s);
|
||||||
|
/** Go back to initial state. */
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
void rewind_subpath();
|
||||||
|
bool next_segment();
|
||||||
|
bool previous_segment();
|
||||||
|
/** Position as calculated by last move/forward/next call. */
|
||||||
|
pixel_position current_position_;
|
||||||
|
/** First pixel of current segment. */
|
||||||
|
pixel_position segment_starting_point_;
|
||||||
|
/** List of all subpaths. */
|
||||||
|
std::vector<segment_vector> subpaths_;
|
||||||
|
/** Currently active subpath. */
|
||||||
|
std::vector<segment_vector>::iterator current_subpath_;
|
||||||
|
/** Current segment for normal operation (move()). */
|
||||||
|
segment_vector::iterator current_segment_;
|
||||||
|
/** Current segment in compatibility mode (vertex(), rewind()). */
|
||||||
|
segment_vector::iterator vertex_segment_;
|
||||||
|
/** Currently active subpath in compatibility mode. */
|
||||||
|
std::vector<segment_vector>::iterator vertex_subpath_;
|
||||||
|
/** State is initialized (after first call to next_subpath()). */
|
||||||
|
bool initialized_;
|
||||||
|
/** Position from start of segment. */
|
||||||
|
double position_in_segment_;
|
||||||
|
/** Angle for current segment. */
|
||||||
|
mutable double angle_;
|
||||||
|
/** Is the value in angle_ valid?
|
||||||
|
* Used to avoid unnecessary calculations. */
|
||||||
|
mutable bool angle_valid_;
|
||||||
|
typedef std::map<double, vertex_cache_ptr> offseted_lines_map;
|
||||||
|
/** Cache of all offseted lines already computed. */
|
||||||
|
offseted_lines_map offseted_lines_;
|
||||||
|
/** Linear position, i.e distance from start of line. */
|
||||||
|
double position_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
vertex_cache::vertex_cache(T &path)
|
||||||
|
: current_position_(),
|
||||||
|
segment_starting_point_(),
|
||||||
|
subpaths_(),
|
||||||
|
current_subpath_(),
|
||||||
|
current_segment_(),
|
||||||
|
vertex_segment_(),
|
||||||
|
vertex_subpath_(),
|
||||||
|
initialized_(false),
|
||||||
|
position_in_segment_(0.),
|
||||||
|
angle_(0.),
|
||||||
|
angle_valid_(false),
|
||||||
|
offseted_lines_(),
|
||||||
|
position_(0.)
|
||||||
|
{
|
||||||
|
path.rewind(0);
|
||||||
|
unsigned cmd;
|
||||||
|
double new_x = 0., new_y = 0., old_x = 0., old_y = 0.;
|
||||||
|
bool first = true; //current_subpath_ uninitalized
|
||||||
|
while (!agg::is_stop(cmd = path.vertex(&new_x, &new_y)))
|
||||||
|
{
|
||||||
|
if (agg::is_move_to(cmd))
|
||||||
|
{
|
||||||
|
//Create new sub path
|
||||||
|
subpaths_.push_back(segment_vector());
|
||||||
|
current_subpath_ = subpaths_.end()-1;
|
||||||
|
current_subpath_->add_segment(new_x, new_y, 0);
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
if (agg::is_line_to(cmd))
|
||||||
|
{
|
||||||
|
if (first)
|
||||||
|
{
|
||||||
|
MAPNIK_LOG_ERROR(vertex_cache) << "No starting point in path!\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
double dx = old_x - new_x;
|
||||||
|
double dy = old_y - new_y;
|
||||||
|
double segment_length = std::sqrt(dx*dx + dy*dy);
|
||||||
|
current_subpath_->add_segment(new_x, new_y, segment_length);
|
||||||
|
}
|
||||||
|
old_x = new_x;
|
||||||
|
old_y = new_y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -33,6 +33,7 @@
|
||||||
// boost
|
// boost
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
|
||||||
// stl
|
// stl
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
@ -60,10 +61,10 @@ struct MAPNIK_DECL text_symbolizer : public symbolizer_base
|
||||||
// not able to compile make_shared used within a constructor
|
// 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(text_placements_ptr placements = text_placements_ptr(new text_placements_dummy));
|
||||||
text_symbolizer(expression_ptr name, std::string const& face_name,
|
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_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_placements_ptr placements = text_placements_ptr(new text_placements_dummy)
|
||||||
);
|
);
|
||||||
text_symbolizer(text_symbolizer const& rhs);
|
text_symbolizer(text_symbolizer const& rhs);
|
||||||
|
@ -90,8 +91,8 @@ struct MAPNIK_DECL text_symbolizer : public symbolizer_base
|
||||||
void set_character_spacing(double spacing);
|
void set_character_spacing(double spacing);
|
||||||
double get_label_spacing() const func_deprecated; // spacing between repeated labels on lines
|
double get_label_spacing() const func_deprecated; // spacing between repeated labels on lines
|
||||||
void set_label_spacing(double spacing);
|
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
|
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(double tolerance);
|
void set_label_position_tolerance(unsigned tolerance);
|
||||||
bool get_force_odd_labels() const func_deprecated; // try render an odd amount of labels
|
bool get_force_odd_labels() const func_deprecated; // try render an odd amount of labels
|
||||||
void set_force_odd_labels(bool force);
|
void set_force_odd_labels(bool force);
|
||||||
double get_max_char_angle_delta() const func_deprecated; // maximum change in angle between adjacent characters
|
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);
|
void set_vertical_alignment(vertical_alignment_e valign);
|
||||||
vertical_alignment_e get_vertical_alignment() const func_deprecated;
|
vertical_alignment_e get_vertical_alignment() const func_deprecated;
|
||||||
void set_displacement(double x, double y);
|
void set_displacement(double x, double y);
|
||||||
void set_displacement(position const& p);
|
void set_displacement(pixel_position const& p);
|
||||||
position const& get_displacement() const func_deprecated;
|
pixel_position const& get_displacement() const func_deprecated;
|
||||||
void set_avoid_edges(bool avoid);
|
void set_avoid_edges(bool avoid);
|
||||||
bool get_avoid_edges() const func_deprecated;
|
bool get_avoid_edges() const func_deprecated;
|
||||||
void set_minimum_distance(double distance);
|
void set_minimum_distance(double distance);
|
||||||
|
|
|
@ -21,15 +21,10 @@
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
// mapnik
|
// mapnik
|
||||||
#include <mapnik/feature.hpp>
|
|
||||||
#include <mapnik/agg_renderer.hpp>
|
#include <mapnik/agg_renderer.hpp>
|
||||||
#include <mapnik/agg_rasterizer.hpp>
|
#include <mapnik/graphics.hpp>
|
||||||
#include <mapnik/image_util.hpp>
|
|
||||||
#include <mapnik/text/symbolizer_helpers.hpp>
|
#include <mapnik/text/symbolizer_helpers.hpp>
|
||||||
#include <mapnik/pixel_position.hpp>
|
#include <mapnik/text/renderer.hpp>
|
||||||
#include <mapnik/text/face.hpp>
|
|
||||||
|
|
||||||
// boost
|
|
||||||
|
|
||||||
namespace mapnik {
|
namespace mapnik {
|
||||||
|
|
||||||
|
@ -39,42 +34,24 @@ void agg_renderer<T>::process(shield_symbolizer const& sym,
|
||||||
proj_transform const& prj_trans)
|
proj_transform const& prj_trans)
|
||||||
{
|
{
|
||||||
box2d<double> clip_box = clipping_extent();
|
box2d<double> clip_box = clipping_extent();
|
||||||
shield_symbolizer_helper<face_manager<freetype_engine>,
|
text_symbolizer_helper helper(
|
||||||
label_collision_detector4> helper(
|
|
||||||
sym, feature, prj_trans,
|
sym, feature, prj_trans,
|
||||||
width_, height_,
|
width_, height_,
|
||||||
scale_factor_,
|
scale_factor_,
|
||||||
t_, font_manager_, *detector_,
|
t_, font_manager_, *detector_,
|
||||||
clip_box);
|
clip_box);
|
||||||
|
|
||||||
text_renderer<T> ren(*current_buffer_,
|
agg_text_renderer<T> ren(*current_buffer_, sym.get_halo_rasterizer(), sym.comp_op(), scale_factor_, font_manager_.get_stroker());
|
||||||
font_manager_,
|
|
||||||
sym.get_halo_rasterizer(),
|
|
||||||
sym.comp_op(),
|
|
||||||
scale_factor_);
|
|
||||||
|
|
||||||
while (helper.next())
|
placements_list const& placements = helper.get();
|
||||||
|
for (glyph_positions_ptr glyphs : placements)
|
||||||
{
|
{
|
||||||
placements_type const& placements = helper.placements();
|
if (glyphs->marker())
|
||||||
for (unsigned int ii = 0; ii < placements.size(); ++ii)
|
render_marker(glyphs->marker_pos(),
|
||||||
{
|
*(glyphs->marker()->marker),
|
||||||
// get_marker_position returns (minx,miny) corner position,
|
glyphs->marker()->transform,
|
||||||
// while (currently only) agg_renderer::render_marker newly
|
sym.get_opacity(), sym.comp_op());
|
||||||
// expects center position;
|
ren.render(*glyphs);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,12 +21,10 @@
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
// mapnik
|
// mapnik
|
||||||
#include <mapnik/feature.hpp>
|
|
||||||
#include <mapnik/agg_renderer.hpp>
|
#include <mapnik/agg_renderer.hpp>
|
||||||
#include <mapnik/agg_rasterizer.hpp>
|
|
||||||
#include <mapnik/text/symbolizer_helpers.hpp>
|
|
||||||
#include <mapnik/graphics.hpp>
|
#include <mapnik/graphics.hpp>
|
||||||
#include <mapnik/text/face.hpp>
|
#include <mapnik/text/symbolizer_helpers.hpp>
|
||||||
|
#include <mapnik/text/renderer.hpp>
|
||||||
|
|
||||||
namespace mapnik {
|
namespace mapnik {
|
||||||
|
|
||||||
|
@ -36,28 +34,19 @@ void agg_renderer<T>::process(text_symbolizer const& sym,
|
||||||
proj_transform const& prj_trans)
|
proj_transform const& prj_trans)
|
||||||
{
|
{
|
||||||
box2d<double> clip_box = clipping_extent();
|
box2d<double> clip_box = clipping_extent();
|
||||||
text_symbolizer_helper<face_manager<freetype_engine>,
|
text_symbolizer_helper helper(
|
||||||
label_collision_detector4> helper(
|
|
||||||
sym, feature, prj_trans,
|
sym, feature, prj_trans,
|
||||||
width_,height_,
|
width_, height_,
|
||||||
scale_factor_,
|
scale_factor_,
|
||||||
t_, font_manager_, *detector_,
|
t_, font_manager_, *detector_,
|
||||||
clip_box);
|
clip_box);
|
||||||
|
|
||||||
text_renderer<T> ren(*current_buffer_,
|
agg_text_renderer<T> ren(*current_buffer_, sym.get_halo_rasterizer(), sym.comp_op(), scale_factor_, font_manager_.get_stroker());
|
||||||
font_manager_,
|
|
||||||
sym.get_halo_rasterizer(),
|
|
||||||
sym.comp_op(),
|
|
||||||
scale_factor_);
|
|
||||||
|
|
||||||
while (helper.next())
|
placements_list const& placements = helper.get();
|
||||||
|
for (glyph_positions_ptr glyphs : placements)
|
||||||
{
|
{
|
||||||
placements_type const& placements = helper.placements();
|
ren.render(*glyphs);
|
||||||
for (unsigned int ii = 0; ii < placements.size(); ++ii)
|
|
||||||
{
|
|
||||||
ren.prepare_glyphs(placements[ii]);
|
|
||||||
ren.render(placements[ii].center);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -360,6 +360,15 @@ bool box2d<T>::valid() const
|
||||||
return (minx_ <= maxx_ && miny_ <= maxy_) ;
|
return (minx_ <= maxx_ && miny_ <= maxy_) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void box2d<T>::move(T x, T y)
|
||||||
|
{
|
||||||
|
minx_ += x;
|
||||||
|
maxx_ += x;
|
||||||
|
miny_ += y;
|
||||||
|
maxy_ += y;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
box2d<T>& box2d<T>::operator+=(box2d<T> const& other)
|
box2d<T>& box2d<T>::operator+=(box2d<T> const& other)
|
||||||
{
|
{
|
||||||
|
@ -367,6 +376,22 @@ box2d<T>& box2d<T>::operator+=(box2d<T> const& other)
|
||||||
return *this;
|
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>
|
template <typename T>
|
||||||
box2d<T>& box2d<T>::operator*=(T t)
|
box2d<T>& box2d<T>::operator*=(T t)
|
||||||
|
|
16
src/build.py
16
src/build.py
|
@ -57,7 +57,7 @@ regex = 'boost_regex%s' % env['BOOST_APPEND']
|
||||||
system = 'boost_system%s' % env['BOOST_APPEND']
|
system = 'boost_system%s' % env['BOOST_APPEND']
|
||||||
|
|
||||||
# clear out and re-set libs for this env
|
# 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']:
|
if '-DMAPNIK_USE_PROJ4' in env['CPPDEFINES']:
|
||||||
lib_env['LIBS'].append('proj')
|
lib_env['LIBS'].append('proj')
|
||||||
|
@ -164,7 +164,6 @@ source = Split(
|
||||||
parse_transform.cpp
|
parse_transform.cpp
|
||||||
palette.cpp
|
palette.cpp
|
||||||
path_expression_grammar.cpp
|
path_expression_grammar.cpp
|
||||||
text/placement_finder.cpp
|
|
||||||
plugin.cpp
|
plugin.cpp
|
||||||
point_symbolizer.cpp
|
point_symbolizer.cpp
|
||||||
polygon_pattern_symbolizer.cpp
|
polygon_pattern_symbolizer.cpp
|
||||||
|
@ -182,7 +181,6 @@ source = Split(
|
||||||
memory_datasource.cpp
|
memory_datasource.cpp
|
||||||
stroke.cpp
|
stroke.cpp
|
||||||
symbolizer.cpp
|
symbolizer.cpp
|
||||||
text/symbolizer_helpers.cpp
|
|
||||||
unicode.cpp
|
unicode.cpp
|
||||||
markers_symbolizer.cpp
|
markers_symbolizer.cpp
|
||||||
raster_colorizer.cpp
|
raster_colorizer.cpp
|
||||||
|
@ -202,7 +200,16 @@ source = Split(
|
||||||
json/feature_parser.cpp
|
json/feature_parser.cpp
|
||||||
json/feature_collection_parser.cpp
|
json/feature_collection_parser.cpp
|
||||||
json/geojson_generator.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/base.cpp
|
||||||
text/formatting/expression.cpp
|
text/formatting/expression.cpp
|
||||||
text/formatting/list.cpp
|
text/formatting/list.cpp
|
||||||
|
@ -214,7 +221,6 @@ source = Split(
|
||||||
text/placements/dummy.cpp
|
text/placements/dummy.cpp
|
||||||
text/placements/list.cpp
|
text/placements/list.cpp
|
||||||
text/placements/simple.cpp
|
text/placements/simple.cpp
|
||||||
text/text_properties.cpp
|
|
||||||
xml_tree.cpp
|
xml_tree.cpp
|
||||||
config_error.cpp
|
config_error.cpp
|
||||||
color_factory.cpp
|
color_factory.cpp
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
#include <mapnik/cairo_context.hpp>
|
#include <mapnik/cairo_context.hpp>
|
||||||
#include <mapnik/text/face.hpp>
|
#include <mapnik/text/face.hpp>
|
||||||
#include <mapnik/text/text_properties.hpp>
|
#include <mapnik/text/text_properties.hpp>
|
||||||
#include <mapnik/text/text_path.hpp>
|
|
||||||
#include <mapnik/font_set.hpp>
|
#include <mapnik/font_set.hpp>
|
||||||
|
|
||||||
#include <cairo-ft.h>
|
#include <cairo-ft.h>
|
||||||
|
@ -221,7 +220,7 @@ void cairo_context::set_line_width(double width)
|
||||||
check_object_status_and_throw_exception(*this);
|
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);
|
std::valarray<double> d(dashes.size() * 2);
|
||||||
dash_array::const_iterator itr = dashes.begin();
|
dash_array::const_iterator itr = dashes.begin();
|
||||||
|
@ -404,100 +403,80 @@ void cairo_context::restore()
|
||||||
check_object_status_and_throw_exception(*this);
|
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;
|
cairo_glyph_t glyph;
|
||||||
glyph.index = index;
|
glyph.index = index;
|
||||||
glyph.x = x;
|
glyph.x = pos.x;
|
||||||
glyph.y = y;
|
glyph.y = pos.y;
|
||||||
|
|
||||||
cairo_show_glyphs(cairo_.get(), &glyph, 1);
|
cairo_show_glyphs(cairo_.get(), &glyph, 1);
|
||||||
check_object_status_and_throw_exception(*this);
|
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;
|
cairo_glyph_t glyph;
|
||||||
glyph.index = index;
|
glyph.index = index;
|
||||||
glyph.x = x;
|
glyph.x = pos.x;
|
||||||
glyph.y = y;
|
glyph.y = pos.y;
|
||||||
|
|
||||||
cairo_glyph_path(cairo_.get(), &glyph, 1);
|
cairo_glyph_path(cairo_.get(), &glyph, 1);
|
||||||
check_object_status_and_throw_exception(*this);
|
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,
|
cairo_face_manager & manager,
|
||||||
face_manager<freetype_engine> & font_manager,
|
face_manager<freetype_engine> & font_manager,
|
||||||
double scale_factor)
|
double scale_factor)
|
||||||
{
|
{
|
||||||
double sx = path.center.x;
|
pixel_position const& base = pos->get_base_point();
|
||||||
double sy = path.center.y;
|
|
||||||
|
|
||||||
path.rewind();
|
//Render halo
|
||||||
|
glyph_positions::const_iterator itr, end = pos->end();
|
||||||
for (std::size_t iii = 0; iii < path.num_nodes(); ++iii)
|
for (itr = pos->begin(); itr != end; itr++)
|
||||||
{
|
{
|
||||||
char_info_ptr c;
|
glyph_info const& glyph = *(itr->glyph);
|
||||||
double x, y, angle;
|
double text_size = glyph.format->text_size * scale_factor;
|
||||||
|
glyph.face->set_character_sizes(text_size);
|
||||||
|
|
||||||
path.vertex(c, x, y, angle);
|
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;
|
||||||
|
|
||||||
face_set_ptr faces = font_manager.get_face_set(c->format->face_name, c->format->fontset);
|
set_font_matrix(matrix);
|
||||||
double text_size = c->format->text_size * scale_factor;
|
set_font_face(manager, glyph.face);
|
||||||
faces->set_character_sizes(text_size);
|
|
||||||
|
|
||||||
glyph_ptr glyph = faces->get_glyph(c->c);
|
glyph_path(glyph.glyph_index, base + ~(itr->pos + glyph.offset.rotate(itr->rot)));
|
||||||
|
set_line_width(2.0 * glyph.format->halo_radius * scale_factor);
|
||||||
if (glyph)
|
set_line_join(ROUND_JOIN);
|
||||||
{
|
set_color(glyph.format->halo_fill);
|
||||||
cairo_matrix_t matrix;
|
stroke();
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
//Render text
|
||||||
path.rewind();
|
for (itr = pos->begin(); itr != end; itr++)
|
||||||
|
|
||||||
for (std::size_t iii = 0; iii < path.num_nodes(); ++iii)
|
|
||||||
{
|
{
|
||||||
char_info_ptr c;
|
glyph_info const& glyph = *(itr->glyph);
|
||||||
double x, y, angle;
|
double text_size = glyph.format->text_size * scale_factor;
|
||||||
|
glyph.face->set_character_sizes(text_size);
|
||||||
|
|
||||||
path.vertex(c, x, y, angle);
|
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;
|
||||||
|
|
||||||
face_set_ptr faces = font_manager.get_face_set(c->format->face_name, c->format->fontset);
|
set_font_matrix(matrix);
|
||||||
double text_size = c->format->text_size * scale_factor;
|
set_font_face(manager, glyph.face);
|
||||||
faces->set_character_sizes(text_size);
|
set_color(glyph.format->fill);
|
||||||
|
show_glyph(glyph.glyph_index, base + ~(itr->pos + glyph.offset.rotate(itr->rot)));
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
} //ns mapnik
|
||||||
}
|
|
||||||
|
|
|
@ -49,7 +49,6 @@
|
||||||
#include <mapnik/expression_evaluator.hpp>
|
#include <mapnik/expression_evaluator.hpp>
|
||||||
#include <mapnik/warp.hpp>
|
#include <mapnik/warp.hpp>
|
||||||
#include <mapnik/config.hpp>
|
#include <mapnik/config.hpp>
|
||||||
#include <mapnik/text/text_path.hpp>
|
|
||||||
#include <mapnik/vertex_converters.hpp>
|
#include <mapnik/vertex_converters.hpp>
|
||||||
#include <mapnik/marker_helpers.hpp>
|
#include <mapnik/marker_helpers.hpp>
|
||||||
#include <mapnik/noncopyable.hpp>
|
#include <mapnik/noncopyable.hpp>
|
||||||
|
@ -61,6 +60,7 @@
|
||||||
#include <cairo-version.h>
|
#include <cairo-version.h>
|
||||||
|
|
||||||
// boost
|
// boost
|
||||||
|
|
||||||
#include <boost/math/special_functions/round.hpp>
|
#include <boost/math/special_functions/round.hpp>
|
||||||
|
|
||||||
// agg
|
// agg
|
||||||
|
@ -711,8 +711,7 @@ void cairo_renderer_base::process(shield_symbolizer const& sym,
|
||||||
mapnik::feature_impl & feature,
|
mapnik::feature_impl & feature,
|
||||||
proj_transform const& prj_trans)
|
proj_transform const& prj_trans)
|
||||||
{
|
{
|
||||||
shield_symbolizer_helper<face_manager<freetype_engine>,
|
text_symbolizer_helper helper(
|
||||||
label_collision_detector4> helper(
|
|
||||||
sym, feature, prj_trans,
|
sym, feature, prj_trans,
|
||||||
width_, height_,
|
width_, height_,
|
||||||
scale_factor_,
|
scale_factor_,
|
||||||
|
@ -721,21 +720,16 @@ void cairo_renderer_base::process(shield_symbolizer const& sym,
|
||||||
cairo_save_restore guard(context_);
|
cairo_save_restore guard(context_);
|
||||||
context_.set_operator(sym.comp_op());
|
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();
|
if (glyphs->marker())
|
||||||
for (unsigned int ii = 0; ii < placements.size(); ++ii)
|
|
||||||
{
|
{
|
||||||
pixel_position pos = helper.get_marker_position(placements[ii]);
|
render_marker(glyphs->marker_pos(),
|
||||||
pos.x += 0.5 * helper.get_marker_width();
|
*(glyphs->marker()->marker), glyphs->marker()->transform,
|
||||||
pos.y += 0.5 * helper.get_marker_height();
|
sym.get_opacity());
|
||||||
render_marker(pos,
|
|
||||||
helper.get_marker(),
|
|
||||||
helper.get_image_transform(),
|
|
||||||
sym.get_opacity());
|
|
||||||
|
|
||||||
context_.add_text(placements[ii], face_manager_, font_manager_, scale_factor_);
|
|
||||||
}
|
}
|
||||||
|
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,
|
mapnik::feature_impl & feature,
|
||||||
proj_transform const& prj_trans)
|
proj_transform const& prj_trans)
|
||||||
{
|
{
|
||||||
text_symbolizer_helper<face_manager<freetype_engine>,
|
text_symbolizer_helper helper(
|
||||||
label_collision_detector4> helper(
|
|
||||||
sym, feature, prj_trans,
|
sym, feature, prj_trans,
|
||||||
width_, height_,
|
width_, height_,
|
||||||
scale_factor_,
|
scale_factor_,
|
||||||
|
@ -1288,13 +1281,10 @@ void cairo_renderer_base::process(text_symbolizer const& sym,
|
||||||
cairo_save_restore guard(context_);
|
cairo_save_restore guard(context_);
|
||||||
context_.set_operator(sym.comp_op());
|
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();
|
context_.add_text(glyphs, face_manager_, font_manager_, scale_factor_);
|
||||||
for (unsigned int ii = 0; ii < placements.size(); ++ii)
|
|
||||||
{
|
|
||||||
context_.add_text(placements[ii], face_manager_, font_manager_, scale_factor_);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,15 +23,6 @@
|
||||||
// mapnik
|
// mapnik
|
||||||
#include <mapnik/debug.hpp>
|
#include <mapnik/debug.hpp>
|
||||||
#include <mapnik/font_engine_freetype.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/pixel_position.hpp>
|
||||||
#include <mapnik/text/face.hpp>
|
#include <mapnik/text/face.hpp>
|
||||||
#include <mapnik/util/fs.hpp>
|
#include <mapnik/util/fs.hpp>
|
||||||
|
@ -41,20 +32,20 @@
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
// stl
|
// stl
|
||||||
#include <sstream>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
// icu
|
// freetype2
|
||||||
#include <unicode/ubidi.h>
|
extern "C"
|
||||||
#include <unicode/ushape.h>
|
{
|
||||||
#include <unicode/schriter.h>
|
#include <ft2build.h>
|
||||||
#include <unicode/uversion.h>
|
#include FT_FREETYPE_H
|
||||||
|
#include FT_STROKER_H
|
||||||
|
}
|
||||||
|
|
||||||
namespace mapnik
|
namespace mapnik
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
freetype_engine::freetype_engine() :
|
freetype_engine::freetype_engine() :
|
||||||
library_(NULL)
|
library_(NULL)
|
||||||
|
|
||||||
|
@ -267,6 +258,7 @@ face_ptr freetype_engine::create_face(std::string const& family_name)
|
||||||
return face_ptr();
|
return face_ptr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
stroker_ptr freetype_engine::create_stroker()
|
stroker_ptr freetype_engine::create_stroker()
|
||||||
{
|
{
|
||||||
FT_Stroker s;
|
FT_Stroker s;
|
||||||
|
@ -278,464 +270,81 @@ stroker_ptr freetype_engine::create_stroker()
|
||||||
return stroker_ptr();
|
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
|
template <typename T>
|
||||||
|
face_ptr face_manager<T>::get_face(const std::string &name)
|
||||||
{
|
{
|
||||||
for ( face_ptr const& face : faces_)
|
face_ptr_cache_type::iterator itr;
|
||||||
{
|
itr = face_ptr_cache_.find(name);
|
||||||
FT_UInt g = face->get_char(c);
|
if (itr != face_ptr_cache_.end())
|
||||||
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)
|
|
||||||
{
|
|
||||||
//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())
|
|
||||||
{
|
{
|
||||||
return itr->second;
|
return itr->second;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
FT_Vector pen;
|
{
|
||||||
FT_Error error;
|
face_ptr face = engine_.create_face(name);
|
||||||
|
if (face)
|
||||||
pen.x = 0;
|
{
|
||||||
pen.y = 0;
|
face_ptr_cache_.insert(make_pair(name,face));
|
||||||
|
}
|
||||||
FT_BBox glyph_bbox;
|
return face;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
void font_face_set::get_string_info(string_info & info, mapnik::value_unicode_string const& ustr, char_properties *format)
|
face_set_ptr face_manager<T>::get_face_set(const std::string &name)
|
||||||
{
|
{
|
||||||
double avg_height = character_dimensions('X').height();
|
face_set_ptr face_set = std::make_shared<font_face_set>();
|
||||||
UErrorCode err = U_ZERO_ERROR;
|
if (face_ptr face = get_face(name))
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
info.set_rtl(true);
|
face_set->add(face);
|
||||||
}
|
}
|
||||||
|
return face_set;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
face_set_ptr face_manager<T>::get_face_set(const font_set &fset)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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
|
#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)
|
|
||||||
{
|
|
||||||
unsigned gray=bitmap->buffer[q*bitmap->width+p];
|
|
||||||
if (gray)
|
|
||||||
{
|
|
||||||
pixmap.composite_pixel(comp_op, i, j, rgba, gray, opacity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return face_set;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void render_halo(T & pixmap,
|
face_set_ptr face_manager<T>::get_face_set(const std::string &name, boost::optional<font_set> fset)
|
||||||
FT_Bitmap *bitmap,
|
|
||||||
unsigned rgba,
|
|
||||||
int x1,
|
|
||||||
int y1,
|
|
||||||
double halo_radius,
|
|
||||||
double opacity,
|
|
||||||
composite_mode_e comp_op)
|
|
||||||
{
|
{
|
||||||
int width = bitmap->width;
|
if (fset && fset->size() > 0)
|
||||||
int height = bitmap->rows;
|
|
||||||
int x, y;
|
|
||||||
if (halo_radius < 1.0)
|
|
||||||
{
|
{
|
||||||
for (x=0; x < width; x++)
|
return get_face_set(*fset);
|
||||||
{
|
}
|
||||||
for (y=0; y < height; y++)
|
else
|
||||||
{
|
{
|
||||||
int gray = bitmap->buffer[y*bitmap->width+x];
|
return get_face_set(name);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void render_halo_id(T & pixmap,
|
|
||||||
FT_Bitmap *bitmap,
|
|
||||||
mapnik::value_integer feature_id,
|
|
||||||
int x1,
|
|
||||||
int y1,
|
|
||||||
int halo_radius)
|
|
||||||
{
|
|
||||||
int width = bitmap->width;
|
|
||||||
int height = bitmap->rows;
|
|
||||||
int x, y;
|
|
||||||
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.setPixel(x+x1+m,y+y1+n,feature_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 )
|
|
||||||
{
|
|
||||||
bbox.xMin = 0;
|
|
||||||
bbox.yMin = 0;
|
|
||||||
bbox.xMax = 0;
|
|
||||||
bbox.yMax = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// take ownership of the glyph
|
|
||||||
glyphs_.push_back(new glyph_t(image, c->format));
|
|
||||||
}
|
|
||||||
|
|
||||||
return box2d<double>(bbox.xMin, bbox.yMin, bbox.xMax, bbox.yMax);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void text_renderer<T>::render(pixel_position const& pos)
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
//render actual text
|
|
||||||
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;
|
|
||||||
composite_bitmap(pixmap_,
|
|
||||||
&bit->bitmap,
|
|
||||||
itr->properties->fill.rgba(),
|
|
||||||
bit->left,
|
|
||||||
height - bit->top,
|
|
||||||
itr->properties->text_opacity,
|
|
||||||
comp_op_
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#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
|
|
||||||
|
|
||||||
#ifdef MAPNIK_THREADSAFE
|
#ifdef MAPNIK_THREADSAFE
|
||||||
std::mutex freetype_engine::mutex_;
|
std::mutex freetype_engine::mutex_;
|
||||||
#endif
|
#endif
|
||||||
std::map<std::string,std::pair<int,std::string> > freetype_engine::name2file_;
|
std::map<std::string,std::pair<int,std::string> > freetype_engine::name2file_;
|
||||||
std::map<std::string,std::string> freetype_engine::memory_fonts_;
|
std::map<std::string,std::string> freetype_engine::memory_fonts_;
|
||||||
|
template class face_manager<freetype_engine>;
|
||||||
|
|
||||||
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&);
|
|
||||||
#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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,14 +22,9 @@
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
// mapnik
|
// mapnik
|
||||||
#include <mapnik/feature.hpp>
|
|
||||||
#include <mapnik/grid/grid_rasterizer.hpp>
|
|
||||||
#include <mapnik/grid/grid_renderer.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/text/symbolizer_helpers.hpp>
|
||||||
#include <mapnik/pixel_position.hpp>
|
#include <mapnik/text/renderer.hpp>
|
||||||
#include <mapnik/text/face.hpp>
|
|
||||||
|
|
||||||
// agg
|
// agg
|
||||||
#include "agg_trans_affine.h"
|
#include "agg_trans_affine.h"
|
||||||
|
@ -41,50 +36,28 @@ void grid_renderer<T>::process(shield_symbolizer const& sym,
|
||||||
mapnik::feature_impl & feature,
|
mapnik::feature_impl & feature,
|
||||||
proj_transform const& prj_trans)
|
proj_transform const& prj_trans)
|
||||||
{
|
{
|
||||||
shield_symbolizer_helper<face_manager<freetype_engine>,
|
text_symbolizer_helper helper(
|
||||||
label_collision_detector4> helper(
|
|
||||||
sym, feature, prj_trans,
|
sym, feature, prj_trans,
|
||||||
width_, height_,
|
width_, height_,
|
||||||
scale_factor_,
|
scale_factor_ * (1.0/pixmap_.get_resolution()),
|
||||||
t_, font_manager_, *detector_,
|
t_, font_manager_, *detector_,
|
||||||
query_extent_);
|
query_extent_);
|
||||||
bool placement_found = false;
|
|
||||||
|
|
||||||
text_renderer<T> ren(pixmap_,
|
grid_text_renderer<T> ren(pixmap_, sym.comp_op(), scale_factor_);
|
||||||
font_manager_,
|
|
||||||
sym.get_halo_rasterizer(),
|
|
||||||
sym.comp_op(),
|
|
||||||
scale_factor_);
|
|
||||||
|
|
||||||
text_placement_info_ptr placement;
|
placements_list const& placements = helper.get();
|
||||||
while (helper.next())
|
if (placements.empty()) return;
|
||||||
|
for (glyph_positions_ptr glyphs : placements)
|
||||||
{
|
{
|
||||||
placement_found = true;
|
if (glyphs->marker()->marker)
|
||||||
placements_type const& placements = helper.placements();
|
render_marker(feature, pixmap_.get_resolution(),
|
||||||
for (unsigned int ii = 0; ii < placements.size(); ++ii)
|
glyphs->marker_pos(),
|
||||||
{
|
*(glyphs->marker()->marker),
|
||||||
// get_marker_position returns (minx,miny) corner position,
|
glyphs->marker()->transform,
|
||||||
// while (currently only) agg_renderer::render_marker newly
|
sym.get_opacity(), sym.comp_op());
|
||||||
// expects center position;
|
ren.render(*glyphs, feature.id());
|
||||||
// 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 (placement_found)
|
pixmap_.add_feature(feature);
|
||||||
pixmap_.add_feature(feature);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template void grid_renderer<grid>::process(shield_symbolizer const&,
|
template void grid_renderer<grid>::process(shield_symbolizer const&,
|
||||||
|
|
|
@ -21,10 +21,9 @@
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
// mapnik
|
// mapnik
|
||||||
#include <mapnik/feature.hpp>
|
|
||||||
#include <mapnik/grid/grid_renderer.hpp>
|
#include <mapnik/grid/grid_renderer.hpp>
|
||||||
#include <mapnik/text/symbolizer_helpers.hpp>
|
#include <mapnik/text/symbolizer_helpers.hpp>
|
||||||
#include <mapnik/text/face.hpp>
|
#include <mapnik/text/renderer.hpp>
|
||||||
|
|
||||||
namespace mapnik {
|
namespace mapnik {
|
||||||
|
|
||||||
|
@ -33,32 +32,22 @@ void grid_renderer<T>::process(text_symbolizer const& sym,
|
||||||
mapnik::feature_impl & feature,
|
mapnik::feature_impl & feature,
|
||||||
proj_transform const& prj_trans)
|
proj_transform const& prj_trans)
|
||||||
{
|
{
|
||||||
text_symbolizer_helper<face_manager<freetype_engine>,
|
text_symbolizer_helper helper(
|
||||||
label_collision_detector4> helper(
|
|
||||||
sym, feature, prj_trans,
|
sym, feature, prj_trans,
|
||||||
width_, height_,
|
width_, height_,
|
||||||
scale_factor_ * (1.0/pixmap_.get_resolution()),
|
scale_factor_ * (1.0/pixmap_.get_resolution()),
|
||||||
t_, font_manager_, *detector_,
|
t_, font_manager_, *detector_,
|
||||||
query_extent_);
|
query_extent_);
|
||||||
bool placement_found = false;
|
|
||||||
|
|
||||||
text_renderer<T> ren(pixmap_,
|
grid_text_renderer<T> ren(pixmap_, sym.comp_op(), scale_factor_);
|
||||||
font_manager_,
|
|
||||||
sym.get_halo_rasterizer(),
|
|
||||||
sym.comp_op(),
|
|
||||||
scale_factor_);
|
|
||||||
|
|
||||||
while (helper.next()) {
|
placements_list const& placements = helper.get();
|
||||||
placement_found = true;
|
if (!placements.size()) return;
|
||||||
placements_type const& placements = helper.placements();
|
for (glyph_positions_ptr glyphs : placements)
|
||||||
for (unsigned int ii = 0; ii < placements.size(); ++ii)
|
{
|
||||||
{
|
ren.render(*glyphs, feature.id());
|
||||||
ren.prepare_glyphs(placements[ii]);
|
|
||||||
ren.render_id(feature.id(), placements[ii].center);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (placement_found) pixmap_.add_feature(feature);
|
pixmap_.add_feature(feature);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template void grid_renderer<grid>::process(text_symbolizer const&,
|
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&);
|
proj_transform const&);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 = std::make_shared<text_placements_dummy>();
|
||||||
placement_finder->defaults.from_xml(sym, fontsets_);
|
placement_finder->defaults.from_xml(sym, fontsets_);
|
||||||
}
|
}
|
||||||
if (strict_ &&
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
text_symbolizer text_symbol = text_symbolizer(placement_finder);
|
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_);
|
placement_finder->defaults.from_xml(sym, fontsets_);
|
||||||
if (strict_ &&
|
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);
|
shield_symbolizer shield_symbol = shield_symbolizer(placement_finder);
|
||||||
|
|
|
@ -228,19 +228,19 @@ public:
|
||||||
set_attr(sym_node, "unlock-image", sym.get_unlock_image());
|
set_attr(sym_node, "unlock-image", sym.get_unlock_image());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sym.get_placement_options()->defaults.format.text_opacity !=
|
if (sym.get_placement_options()->defaults.format->text_opacity !=
|
||||||
dfl.get_placement_options()->defaults.format.text_opacity || explicit_defaults_)
|
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();
|
pixel_position displacement = sym.get_shield_displacement();
|
||||||
if (displacement.first != dfl.get_shield_displacement().first || explicit_defaults_)
|
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())
|
if (sym.get_image_transform())
|
||||||
{
|
{
|
||||||
|
|
|
@ -76,10 +76,10 @@ bool shield_symbolizer::get_unlock_image() const
|
||||||
|
|
||||||
void shield_symbolizer::set_shield_displacement(double shield_dx,double shield_dy)
|
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_;
|
return shield_displacement_;
|
||||||
}
|
}
|
||||||
|
|
140
src/text/face.cpp
Normal file
140
src/text/face.cpp
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
// mapnik
|
||||||
|
#include <mapnik/text/face.hpp>
|
||||||
|
#include <mapnik/debug.hpp>
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#include FT_GLYPH_H
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace mapnik
|
||||||
|
{
|
||||||
|
|
||||||
|
font_face::font_face(FT_Face face)
|
||||||
|
: face_(face), dimension_cache_(), char_height_(0.0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
double font_face::get_char_height() const
|
||||||
|
{
|
||||||
|
if (char_height_ != 0.0) return char_height_;
|
||||||
|
glyph_info tmp;
|
||||||
|
tmp.glyph_index = FT_Get_Char_Index(face_, 'X');
|
||||||
|
glyph_dimensions(tmp);
|
||||||
|
char_height_ = tmp.height();
|
||||||
|
return char_height_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool font_face::set_character_sizes(double size)
|
||||||
|
{
|
||||||
|
char_height_ = 0.0;
|
||||||
|
return !FT_Set_Char_Size(face_,0,(FT_F26Dot6)(size * (1<<6)),0,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void font_face::glyph_dimensions(glyph_info & glyph) const
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
//Check if char is already in cache
|
||||||
|
// std::map<glyph_index_t, glyph_info>::const_iterator itr;
|
||||||
|
// itr = dimension_cache_.find(glyph.glyph_index);
|
||||||
|
// if (itr != dimension_cache_.end()) {
|
||||||
|
// glyph = itr->second;
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
FT_Vector pen;
|
||||||
|
pen.x = 0;
|
||||||
|
pen.y = 0;
|
||||||
|
/*
|
||||||
|
FT_Matrix matrix;
|
||||||
|
matrix.xx = (FT_Fixed)( 1 * 0x10000L );
|
||||||
|
matrix.xy = (FT_Fixed)( 0 * 0x10000L );
|
||||||
|
matrix.yx = (FT_Fixed)( 0 * 0x10000L );
|
||||||
|
matrix.yy = (FT_Fixed)( 1 * 0x10000L );
|
||||||
|
FT_Set_Transform(face_, &matrix, &pen);
|
||||||
|
*/
|
||||||
|
// TODO - any benefit to using a matrix here?
|
||||||
|
FT_Set_Transform(face_, 0, &pen);
|
||||||
|
|
||||||
|
if (FT_Load_Glyph (face_, glyph.glyph_index, FT_LOAD_NO_HINTING)) return;
|
||||||
|
|
||||||
|
FT_Glyph image;
|
||||||
|
if (FT_Get_Glyph(face_->glyph, &image)) return;
|
||||||
|
FT_BBox glyph_bbox;
|
||||||
|
FT_Glyph_Get_CBox(image, ft_glyph_bbox_pixels, &glyph_bbox);
|
||||||
|
FT_Done_Glyph(image);
|
||||||
|
|
||||||
|
glyph.ymin = glyph_bbox.yMin; //pixels!
|
||||||
|
glyph.ymax = glyph_bbox.yMax;
|
||||||
|
glyph.line_height = face_->size->metrics.height/64.0;
|
||||||
|
// TODO: we round to integers for now to maintain
|
||||||
|
// back compatibility with Mapnik 2.x
|
||||||
|
//glyph.width = face_->glyph->advance.x/64.0;
|
||||||
|
glyph.width = face_->glyph->advance.x >> 6;
|
||||||
|
|
||||||
|
//TODO: dimension_cache_.insert(std::pair<unsigned, char_info>(c, dim));
|
||||||
|
}
|
||||||
|
|
||||||
|
font_face::~font_face()
|
||||||
|
{
|
||||||
|
MAPNIK_LOG_DEBUG(font_face) <<
|
||||||
|
"font_face: Clean up face \"" << family_name() <<
|
||||||
|
" " << style_name() << "\"";
|
||||||
|
|
||||||
|
FT_Done_Face(face_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
void font_face_set::add(face_ptr face)
|
||||||
|
{
|
||||||
|
faces_.push_back(face);
|
||||||
|
}
|
||||||
|
|
||||||
|
void font_face_set::set_character_sizes(double size)
|
||||||
|
{
|
||||||
|
for (face_ptr const& face : faces_)
|
||||||
|
{
|
||||||
|
face->set_character_sizes(size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
void stroker::init(double radius)
|
||||||
|
{
|
||||||
|
FT_Stroker_Set(s_, (FT_Fixed) (radius * (1<<6)),
|
||||||
|
FT_STROKER_LINECAP_ROUND,
|
||||||
|
FT_STROKER_LINEJOIN_ROUND,
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
|
stroker::~stroker()
|
||||||
|
{
|
||||||
|
MAPNIK_LOG_DEBUG(font_engine_freetype) << "stroker: Destroy stroker=" << s_;
|
||||||
|
|
||||||
|
FT_Stroker_Done(s_);
|
||||||
|
}
|
||||||
|
|
||||||
|
}//ns mapnik
|
|
@ -27,10 +27,12 @@
|
||||||
#include <mapnik/expression_string.hpp>
|
#include <mapnik/expression_string.hpp>
|
||||||
#include <mapnik/expression_evaluator.hpp>
|
#include <mapnik/expression_evaluator.hpp>
|
||||||
#include <mapnik/text/text_properties.hpp>
|
#include <mapnik/text/text_properties.hpp>
|
||||||
|
#include <mapnik/color_factory.hpp>
|
||||||
#include <mapnik/feature.hpp>
|
#include <mapnik/feature.hpp>
|
||||||
#include <mapnik/xml_node.hpp>
|
#include <mapnik/xml_node.hpp>
|
||||||
|
|
||||||
// boost
|
//boost
|
||||||
|
|
||||||
#include <boost/property_tree/ptree.hpp>
|
#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 (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 (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 (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 (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 (fill) set_attr(new_node, "fill", to_expression_string(*fill));
|
||||||
if (halo_fill) set_attr(new_node, "halo-fill", to_expression_string(*halo_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->character_spacing = get_expression(xml, "character-spacing");
|
||||||
n->line_spacing = get_expression(xml, "line-spacing");
|
n->line_spacing = get_expression(xml, "line-spacing");
|
||||||
n->text_opacity = get_expression(xml, "opacity");
|
n->text_opacity = get_expression(xml, "opacity");
|
||||||
n->wrap_before = get_expression(xml, "wrap-before");
|
|
||||||
n->wrap_char = get_expression(xml, "wrap-character");
|
n->wrap_char = get_expression(xml, "wrap-character");
|
||||||
n->fill = get_expression(xml, "fill");
|
n->fill = get_expression(xml, "fill");
|
||||||
n->halo_fill = get_expression(xml, "halo-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;
|
char_properties_ptr new_properties = std::make_shared<char_properties>(*p);
|
||||||
if (face_name) new_properties.face_name =
|
if (face_name) new_properties->face_name =
|
||||||
boost::apply_visitor(evaluate<feature_impl,value_type>(feature), *face_name).to_string();
|
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();
|
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();
|
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();
|
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();
|
boost::apply_visitor(evaluate<feature_impl,value_type>(feature), *text_opacity).to_double();
|
||||||
if (wrap_before) new_properties.wrap_before =
|
if (wrap_char) new_properties->wrap_char =
|
||||||
boost::apply_visitor(evaluate<feature_impl,value_type>(feature), *wrap_before).to_bool();
|
|
||||||
if (wrap_char) new_properties.wrap_char =
|
|
||||||
boost::apply_visitor(evaluate<feature_impl,value_type>(feature), *character_spacing).to_unicode()[0];
|
boost::apply_visitor(evaluate<feature_impl,value_type>(feature), *character_spacing).to_unicode()[0];
|
||||||
// if (fill) new_properties.fill =
|
if (fill) new_properties->fill = parse_color(
|
||||||
// boost::apply_visitor(evaluate<feature_impl,value_type>(feature), *fill).to_color();
|
boost::apply_visitor(evaluate<feature_impl,value_type>(feature), *fill).to_string());
|
||||||
// if (halo_fill) new_properties.halo_fill =
|
if (halo_fill) new_properties->halo_fill = parse_color(
|
||||||
// boost::apply_visitor(evaluate<feature_impl,value_type>(feature), *halo_fill).to_color();
|
boost::apply_visitor(evaluate<feature_impl,value_type>(feature), *halo_fill).to_string());
|
||||||
if (halo_radius) new_properties.halo_radius =
|
if (halo_radius) new_properties->halo_radius =
|
||||||
boost::apply_visitor(evaluate<feature_impl,value_type>(feature), *halo_radius).to_double();
|
boost::apply_visitor(evaluate<feature_impl,value_type>(feature), *halo_radius).to_double();
|
||||||
|
|
||||||
if (child_) {
|
if (child_) {
|
||||||
|
@ -134,7 +132,6 @@ void expression_format::add_expressions(expression_set &output) const
|
||||||
output.insert(character_spacing);
|
output.insert(character_spacing);
|
||||||
output.insert(line_spacing);
|
output.insert(line_spacing);
|
||||||
output.insert(text_opacity);
|
output.insert(text_opacity);
|
||||||
output.insert(wrap_before);
|
|
||||||
output.insert(wrap_char);
|
output.insert(wrap_char);
|
||||||
output.insert(fill);
|
output.insert(fill);
|
||||||
output.insert(halo_fill);
|
output.insert(halo_fill);
|
||||||
|
|
|
@ -27,7 +27,8 @@
|
||||||
#include <mapnik/ptree_helpers.hpp>
|
#include <mapnik/ptree_helpers.hpp>
|
||||||
#include <mapnik/xml_node.hpp>
|
#include <mapnik/xml_node.hpp>
|
||||||
|
|
||||||
// boost
|
//boost
|
||||||
|
|
||||||
#include <boost/property_tree/ptree.hpp>
|
#include <boost/property_tree/ptree.hpp>
|
||||||
|
|
||||||
namespace mapnik {
|
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->character_spacing = xml.get_opt_attr<double>("character-spacing");
|
||||||
n->line_spacing = xml.get_opt_attr<double>("line-spacing");
|
n->line_spacing = xml.get_opt_attr<double>("line-spacing");
|
||||||
n->text_opacity = xml.get_opt_attr<double>("opacity");
|
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->wrap_char = xml.get_opt_attr<unsigned>("wrap-character");
|
||||||
n->text_transform = xml.get_opt_attr<text_transform_e>("text-transform");
|
n->text_transform = xml.get_opt_attr<text_transform_e>("text-transform");
|
||||||
n->fill = xml.get_opt_attr<color>("fill");
|
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;
|
char_properties_ptr new_properties = std::make_shared<char_properties>(*p);
|
||||||
if (face_name) new_properties.face_name = *face_name;
|
if (face_name) new_properties->face_name = *face_name;
|
||||||
if (text_size) new_properties.text_size = *text_size;
|
if (text_size) new_properties->text_size = *text_size;
|
||||||
if (character_spacing) new_properties.character_spacing = *character_spacing;
|
if (character_spacing) new_properties->character_spacing = *character_spacing;
|
||||||
if (line_spacing) new_properties.line_spacing = *line_spacing;
|
if (line_spacing) new_properties->line_spacing = *line_spacing;
|
||||||
if (text_opacity) new_properties.text_opacity = *text_opacity;
|
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 (wrap_char) new_properties.wrap_char = *wrap_char;
|
if (text_transform) new_properties->text_transform = *text_transform;
|
||||||
if (text_transform) new_properties.text_transform = *text_transform;
|
if (fill) new_properties->fill = *fill;
|
||||||
if (fill) new_properties.fill = *fill;
|
if (halo_fill) new_properties->halo_fill = *halo_fill;
|
||||||
if (halo_fill) new_properties.halo_fill = *halo_fill;
|
if (halo_radius) new_properties->halo_radius = *halo_radius;
|
||||||
if (halo_radius) new_properties.halo_radius = *halo_radius;
|
|
||||||
|
|
||||||
if (child_) {
|
if (child_) {
|
||||||
child_->apply(new_properties, feature, output);
|
child_->apply(new_properties, feature, output);
|
||||||
|
|
|
@ -32,7 +32,6 @@ namespace mapnik {
|
||||||
using boost::property_tree::ptree;
|
using boost::property_tree::ptree;
|
||||||
|
|
||||||
namespace formatting {
|
namespace formatting {
|
||||||
/************************************************************/
|
|
||||||
|
|
||||||
void list_node::to_xml(boost::property_tree::ptree & xml) const
|
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_)
|
for (node_ptr const& node : children_)
|
||||||
{
|
{
|
||||||
|
|
|
@ -25,13 +25,16 @@
|
||||||
#include <mapnik/expression_evaluator.hpp>
|
#include <mapnik/expression_evaluator.hpp>
|
||||||
#include <mapnik/feature.hpp>
|
#include <mapnik/feature.hpp>
|
||||||
#include <mapnik/text/text_properties.hpp>
|
#include <mapnik/text/text_properties.hpp>
|
||||||
#include <mapnik/text/processed_text.hpp>
|
|
||||||
#include <mapnik/xml_node.hpp>
|
#include <mapnik/xml_node.hpp>
|
||||||
#include <mapnik/value_types.hpp>
|
#include <mapnik/text/layout.hpp>
|
||||||
|
|
||||||
|
//boost
|
||||||
|
|
||||||
|
|
||||||
// boost
|
// boost
|
||||||
#include <boost/property_tree/ptree.hpp>
|
#include <boost/property_tree/ptree.hpp>
|
||||||
|
|
||||||
|
|
||||||
namespace mapnik
|
namespace mapnik
|
||||||
{
|
{
|
||||||
namespace formatting
|
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>());
|
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();
|
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();
|
text_str = text_str.toUpper();
|
||||||
}
|
}
|
||||||
else if (p.text_transform == LOWERCASE)
|
else if (p->text_transform == LOWERCASE)
|
||||||
{
|
{
|
||||||
text_str = text_str.toLower();
|
text_str = text_str.toLower();
|
||||||
}
|
}
|
||||||
#if !UCONFIG_NO_BREAK_ITERATION
|
#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
|
// note: requires BreakIterator support in ICU which is optional
|
||||||
text_str = text_str.toTitle(NULL);
|
text_str = text_str.toTitle(NULL);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (text_str.length() > 0) {
|
if (text_str.length() > 0) {
|
||||||
output.push_back(p, text_str);
|
output.add_text(text_str, p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
200
src/text/itemizer.cpp
Normal file
200
src/text/itemizer.cpp
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
//mapnik
|
||||||
|
#include <mapnik/text/itemizer.hpp>
|
||||||
|
#include <mapnik/text/scrptrun.hpp>
|
||||||
|
#include <mapnik/debug.hpp>
|
||||||
|
|
||||||
|
// stl
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace mapnik
|
||||||
|
{
|
||||||
|
|
||||||
|
text_itemizer::text_itemizer()
|
||||||
|
: text_(), format_runs_(), direction_runs_(), script_runs_()
|
||||||
|
{
|
||||||
|
forced_line_breaks_.push_back(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void text_itemizer::add_text(mapnik::value_unicode_string str, char_properties_ptr format)
|
||||||
|
{
|
||||||
|
unsigned start = text_.length();
|
||||||
|
text_ += str;
|
||||||
|
format_runs_.emplace_back(format, start, text_.length());
|
||||||
|
|
||||||
|
while ((start = text_.indexOf('\n', start)+1) > 0)
|
||||||
|
{
|
||||||
|
forced_line_breaks_.push_back(start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<text_item> const& text_itemizer::itemize(unsigned start, unsigned end)
|
||||||
|
{
|
||||||
|
if (end == 0) {
|
||||||
|
end = text_.length();
|
||||||
|
}
|
||||||
|
// format itemiziation is done by add_text()
|
||||||
|
itemize_direction(start, end);
|
||||||
|
itemize_script();
|
||||||
|
create_item_list();
|
||||||
|
return output_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void text_itemizer::clear()
|
||||||
|
{
|
||||||
|
output_.clear();
|
||||||
|
text_.remove();
|
||||||
|
format_runs_.clear();
|
||||||
|
forced_line_breaks_.clear();
|
||||||
|
forced_line_breaks_.push_back(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<unsigned, unsigned> text_itemizer::line(unsigned i) const
|
||||||
|
{
|
||||||
|
#ifdef MAPNIK_DEBUG
|
||||||
|
if (i >= forced_line_breaks_.size()) return std::make_pair(0, 0);
|
||||||
|
#endif
|
||||||
|
if (i == forced_line_breaks_.size()-1)
|
||||||
|
{
|
||||||
|
return std::make_pair(forced_line_breaks_[i], text_.length());
|
||||||
|
}
|
||||||
|
//Note -1 offset to exclude the \n char
|
||||||
|
return std::make_pair(forced_line_breaks_[i], forced_line_breaks_[i+1]-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned text_itemizer::num_lines() const
|
||||||
|
{
|
||||||
|
return forced_line_breaks_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void text_itemizer::itemize_direction(unsigned start, unsigned end)
|
||||||
|
{
|
||||||
|
direction_runs_.clear();
|
||||||
|
UErrorCode error = U_ZERO_ERROR;
|
||||||
|
int32_t length = end - start;
|
||||||
|
UBiDi *bidi = ubidi_openSized(length, 0, &error);
|
||||||
|
if (!bidi || U_FAILURE(error))
|
||||||
|
{
|
||||||
|
MAPNIK_LOG_ERROR(text_itemizer) << "Failed to create bidi object: " << u_errorName(error) << "\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ubidi_setPara(bidi, text_.getBuffer() + start, length, UBIDI_DEFAULT_LTR, 0, &error);
|
||||||
|
if (U_SUCCESS(error))
|
||||||
|
{
|
||||||
|
UBiDiDirection direction = ubidi_getDirection(bidi);
|
||||||
|
if (direction != UBIDI_MIXED)
|
||||||
|
{
|
||||||
|
direction_runs_.emplace_back(direction, start, end);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// mixed-directional
|
||||||
|
int32_t count = ubidi_countRuns(bidi, &error);
|
||||||
|
if(U_SUCCESS(error))
|
||||||
|
{
|
||||||
|
for(int i=0; i<count; ++i)
|
||||||
|
{
|
||||||
|
int32_t length;
|
||||||
|
int32_t run_start;
|
||||||
|
direction = ubidi_getVisualRun(bidi, i, &run_start, &length);
|
||||||
|
run_start += start; //Add offset to compensate offset in setPara
|
||||||
|
direction_runs_.emplace_back(direction, run_start, run_start+length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MAPNIK_LOG_ERROR(text_itemizer) << "ICU error: " << u_errorName(error) << "\n"; //TODO: Exception
|
||||||
|
}
|
||||||
|
ubidi_close(bidi);
|
||||||
|
}
|
||||||
|
|
||||||
|
void text_itemizer::itemize_script()
|
||||||
|
{
|
||||||
|
script_runs_.clear();
|
||||||
|
|
||||||
|
ScriptRun runs(text_.getBuffer(), text_.length());
|
||||||
|
while (runs.next())
|
||||||
|
{
|
||||||
|
script_runs_.emplace_back(runs.getScriptCode(), runs.getScriptStart(), runs.getScriptEnd());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename T::const_iterator text_itemizer::find_run(T const& list, unsigned position)
|
||||||
|
{
|
||||||
|
typename T::const_iterator itr = list.begin(), end = list.end();
|
||||||
|
for ( ;itr!=end; ++itr)
|
||||||
|
{
|
||||||
|
// end is the first character not included in text range!
|
||||||
|
if (itr->start <= position && itr->end > position) return itr;
|
||||||
|
}
|
||||||
|
return itr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void text_itemizer::create_item_list()
|
||||||
|
{
|
||||||
|
/* This function iterates over direction runs in visual order and splits them if neccessary.
|
||||||
|
* Split RTL runs are processed in reverse order to keep glyphs in correct order.
|
||||||
|
*
|
||||||
|
* logical 123 | 456789
|
||||||
|
* LTR visual 123 | 456789
|
||||||
|
* RTL visual 987654 | 321
|
||||||
|
* Glyphs within a single run are reversed by the shaper.
|
||||||
|
*/
|
||||||
|
output_.clear();
|
||||||
|
for (auto const& dir_run : direction_runs_)
|
||||||
|
{
|
||||||
|
unsigned position = dir_run.start;
|
||||||
|
unsigned end = dir_run.end;
|
||||||
|
std::list<text_item>::iterator rtl_insertion_point = output_.end();
|
||||||
|
// Find first script and format run
|
||||||
|
format_run_list::const_iterator format_itr = find_run(format_runs_, position);
|
||||||
|
script_run_list::const_iterator script_itr = find_run(script_runs_, position);
|
||||||
|
while (position < end)
|
||||||
|
{
|
||||||
|
assert(script_itr != script_runs_.end());
|
||||||
|
assert(format_itr != format_runs_.end());
|
||||||
|
text_item item;
|
||||||
|
item.start = position;
|
||||||
|
position = std::min(script_itr->end, std::min(format_itr->end, end));
|
||||||
|
item.end = position;
|
||||||
|
item.format = format_itr->data;
|
||||||
|
item.script = script_itr->data;
|
||||||
|
item.rtl = dir_run.data;
|
||||||
|
|
||||||
|
if (dir_run.data == UBIDI_LTR)
|
||||||
|
{
|
||||||
|
output_.push_back(item);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rtl_insertion_point = output_.insert(rtl_insertion_point, item);
|
||||||
|
}
|
||||||
|
if (script_itr->end == position) ++script_itr;
|
||||||
|
if (format_itr->end == position) ++format_itr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} //ns mapnik
|
200
src/text/layout.cpp
Normal file
200
src/text/layout.cpp
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* 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/layout.hpp>
|
||||||
|
#include <mapnik/text/text_properties.hpp>
|
||||||
|
#include <mapnik/debug.hpp>
|
||||||
|
|
||||||
|
// ICU
|
||||||
|
#include <unicode/brkiter.h>
|
||||||
|
|
||||||
|
namespace mapnik
|
||||||
|
{
|
||||||
|
|
||||||
|
text_layout::text_layout(face_manager_freetype & font_manager, double scale_factor)
|
||||||
|
: font_manager_(font_manager),
|
||||||
|
scale_factor_(scale_factor),
|
||||||
|
itemizer_(),
|
||||||
|
width_map_(),
|
||||||
|
width_(0.0),
|
||||||
|
height_(0.0),
|
||||||
|
glyphs_count_(0),
|
||||||
|
lines_()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void text_layout::add_text(mapnik::value_unicode_string const& str, char_properties_ptr format)
|
||||||
|
{
|
||||||
|
itemizer_.add_text(str, format);
|
||||||
|
}
|
||||||
|
|
||||||
|
mapnik::value_unicode_string const& text_layout::text() const
|
||||||
|
{
|
||||||
|
return itemizer_.text();
|
||||||
|
}
|
||||||
|
|
||||||
|
void text_layout::layout(double wrap_width, unsigned text_ratio, bool wrap_before)
|
||||||
|
{
|
||||||
|
unsigned num_lines = itemizer_.num_lines();
|
||||||
|
for (unsigned i = 0; i < num_lines; ++i)
|
||||||
|
{
|
||||||
|
std::pair<unsigned, unsigned> line_limits = itemizer_.line(i);
|
||||||
|
text_line line(line_limits.first, line_limits.second);
|
||||||
|
break_line(line, wrap_width, text_ratio, wrap_before); //Break line if neccessary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* In the Unicode string characters are always stored in logical order.
|
||||||
|
* This makes line breaking easy. One word is added to the current line at a time. Once the line is too long
|
||||||
|
* we either go back one step or inset the line break at the current position (depending on "wrap_before" setting).
|
||||||
|
* At the end everything that is left over is added as the final line. */
|
||||||
|
void text_layout::break_line(text_line & line, double wrap_width, unsigned text_ratio, bool wrap_before)
|
||||||
|
{
|
||||||
|
shape_text(line);
|
||||||
|
if (!wrap_width || line.width() < wrap_width)
|
||||||
|
{
|
||||||
|
add_line(line);
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
if (text_ratio)
|
||||||
|
{
|
||||||
|
double wrap_at;
|
||||||
|
double string_width = line.width();
|
||||||
|
double string_height = line.line_height();
|
||||||
|
for (double i = 1.0; ((wrap_at = string_width/i)/(string_height*i)) > text_ratio && (string_width/i) > wrap_width; i += 1.0) ;
|
||||||
|
wrap_width = wrap_at;
|
||||||
|
}
|
||||||
|
|
||||||
|
mapnik::value_unicode_string const& text = itemizer_.text();
|
||||||
|
Locale locale; // TODO: Is the default constructor correct?
|
||||||
|
UErrorCode status = U_ZERO_ERROR;
|
||||||
|
BreakIterator *breakitr = BreakIterator::createLineInstance(locale, status);
|
||||||
|
|
||||||
|
// Not breaking the text if an error occurs is probably the best thing we can do.
|
||||||
|
// https://github.com/mapnik/mapnik/issues/2072
|
||||||
|
if (!U_SUCCESS(status))
|
||||||
|
{
|
||||||
|
add_line(line);
|
||||||
|
MAPNIK_LOG_ERROR(text_layout) << " could not create BreakIterator: " << u_errorName(status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
breakitr->setText(text);
|
||||||
|
|
||||||
|
double current_line_length = 0;
|
||||||
|
int last_break_position = static_cast<int>(line.first_char());
|
||||||
|
for (unsigned i=line.first_char(); i < line.last_char(); ++i)
|
||||||
|
{
|
||||||
|
// TODO: character_spacing
|
||||||
|
std::map<unsigned, double>::const_iterator width_itr = width_map_.find(i);
|
||||||
|
if (width_itr != width_map_.end())
|
||||||
|
{
|
||||||
|
current_line_length += width_itr->second;
|
||||||
|
}
|
||||||
|
if (current_line_length <= wrap_width) continue;
|
||||||
|
/***********************************************/
|
||||||
|
|
||||||
|
|
||||||
|
int break_position = wrap_before ? breakitr->preceding(i) : breakitr->following(i);
|
||||||
|
// following() returns a break position after the last word. So DONE should only be returned
|
||||||
|
// when calling preceding.
|
||||||
|
if (break_position <= last_break_position || break_position == static_cast<int>(BreakIterator::DONE))
|
||||||
|
{
|
||||||
|
// A single word is longer than the maximum line width.
|
||||||
|
// Violate line width requirement and choose next break position
|
||||||
|
break_position = breakitr->following(i);
|
||||||
|
if (break_position == static_cast<int>(BreakIterator::DONE))
|
||||||
|
{
|
||||||
|
break_position = line.last_char();
|
||||||
|
MAPNIK_LOG_ERROR(text_layout) << "Unexpected result in break_line. Trying to recover...\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Break iterator operates on the whole string, while we only look at one line. So we need to
|
||||||
|
// clamp break values.
|
||||||
|
if (break_position < static_cast<int>(line.first_char()))
|
||||||
|
{
|
||||||
|
break_position = line.first_char();
|
||||||
|
}
|
||||||
|
if (break_position > static_cast<int>(line.last_char()))
|
||||||
|
{
|
||||||
|
break_position = line.last_char();
|
||||||
|
}
|
||||||
|
|
||||||
|
text_line new_line(last_break_position, break_position);
|
||||||
|
clear_cluster_widths(last_break_position, break_position);
|
||||||
|
shape_text(new_line);
|
||||||
|
add_line(new_line);
|
||||||
|
last_break_position = break_position;
|
||||||
|
i = break_position - 1;
|
||||||
|
current_line_length = 0;
|
||||||
|
}
|
||||||
|
if (last_break_position == static_cast<int>(line.first_char()))
|
||||||
|
{
|
||||||
|
// No line breaks => no reshaping required
|
||||||
|
add_line(line);
|
||||||
|
}
|
||||||
|
else if (last_break_position != static_cast<int>(line.last_char()))
|
||||||
|
{
|
||||||
|
text_line new_line(last_break_position, line.last_char());
|
||||||
|
clear_cluster_widths(last_break_position, line.last_char());
|
||||||
|
shape_text(new_line);
|
||||||
|
add_line(new_line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void text_layout::add_line(text_line & line)
|
||||||
|
{
|
||||||
|
if (lines_.empty())
|
||||||
|
{
|
||||||
|
line.set_first_line(true);
|
||||||
|
}
|
||||||
|
height_ += line.height();
|
||||||
|
glyphs_count_ += line.size();
|
||||||
|
width_ = std::max(width_, line.width());
|
||||||
|
lines_.push_back(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
void text_layout::clear_cluster_widths(unsigned first, unsigned last)
|
||||||
|
{
|
||||||
|
for (unsigned i=first; i<last; ++i)
|
||||||
|
{
|
||||||
|
width_map_[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void text_layout::clear()
|
||||||
|
{
|
||||||
|
itemizer_.clear();
|
||||||
|
lines_.clear();
|
||||||
|
width_map_.clear();
|
||||||
|
width_ = 0.;
|
||||||
|
height_ = 0.;
|
||||||
|
}
|
||||||
|
|
||||||
|
void text_layout::shape_text(text_line & line)
|
||||||
|
{
|
||||||
|
shaper_type::shape_text(line, itemizer_, width_map_, font_manager_, scale_factor_);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} //ns mapnik
|
File diff suppressed because it is too large
Load diff
|
@ -19,9 +19,11 @@
|
||||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*
|
*
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
// mapnik
|
||||||
#include <mapnik/text/placements/base.hpp>
|
#include <mapnik/text/placements/base.hpp>
|
||||||
|
|
||||||
namespace mapnik {
|
namespace mapnik {
|
||||||
|
|
||||||
text_placements::text_placements() : defaults()
|
text_placements::text_placements() : defaults()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -39,7 +41,7 @@ text_placement_info::text_placement_info(text_placements const* parent,
|
||||||
: properties(parent->defaults),
|
: properties(parent->defaults),
|
||||||
scale_factor(scale_factor_)
|
scale_factor(scale_factor_)
|
||||||
{
|
{
|
||||||
|
properties.format = std::make_shared<char_properties>(*(properties.format));
|
||||||
}
|
}
|
||||||
|
|
||||||
} //ns mapnik
|
} //ns mapnik
|
||||||
|
|
|
@ -27,7 +27,6 @@
|
||||||
//boost
|
//boost
|
||||||
#include <boost/property_tree/ptree.hpp>
|
#include <boost/property_tree/ptree.hpp>
|
||||||
|
|
||||||
|
|
||||||
namespace mapnik
|
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;
|
if (itr->is_text() || !itr->is("Placement")) continue;
|
||||||
text_symbolizer_properties &p = list->add();
|
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);
|
p.from_xml(*itr, fontsets);
|
||||||
//TODO: if (strict_ &&
|
//TODO: if (strict_ &&
|
||||||
// !p.format.fontset.size())
|
// !p.format.fontset.size())
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include <boost/spirit/include/qi.hpp>
|
#include <boost/spirit/include/qi.hpp>
|
||||||
#include <boost/spirit/include/phoenix_core.hpp>
|
#include <boost/spirit/include/phoenix_core.hpp>
|
||||||
#include <boost/spirit/include/phoenix_stl.hpp>
|
#include <boost/spirit/include/phoenix_stl.hpp>
|
||||||
|
|
||||||
#include <boost/property_tree/ptree.hpp>
|
#include <boost/property_tree/ptree.hpp>
|
||||||
|
|
||||||
namespace mapnik
|
namespace mapnik
|
||||||
|
@ -48,7 +49,7 @@ bool text_placement_info_simple::next()
|
||||||
if (state > 0)
|
if (state > 0)
|
||||||
{
|
{
|
||||||
if (state > parent_->text_sizes_.size()) return false;
|
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()) {
|
if (!next_position_only()) {
|
||||||
state++;
|
state++;
|
||||||
|
@ -62,8 +63,8 @@ bool text_placement_info_simple::next()
|
||||||
|
|
||||||
bool text_placement_info_simple::next_position_only()
|
bool text_placement_info_simple::next_position_only()
|
||||||
{
|
{
|
||||||
const position &pdisp = parent_->defaults.displacement;
|
pixel_position const& pdisp = parent_->defaults.displacement;
|
||||||
position &displacement = properties.displacement;
|
pixel_position &displacement = properties.displacement;
|
||||||
if (position_state >= parent_->direction_.size()) return false;
|
if (position_state >= parent_->direction_.size()) return false;
|
||||||
directions_t dir = parent_->direction_[position_state];
|
directions_t dir = parent_->direction_[position_state];
|
||||||
switch (dir) {
|
switch (dir) {
|
||||||
|
@ -71,28 +72,28 @@ bool text_placement_info_simple::next_position_only()
|
||||||
displacement = pdisp;
|
displacement = pdisp;
|
||||||
break;
|
break;
|
||||||
case NORTH:
|
case NORTH:
|
||||||
displacement = std::make_pair(0, -abs(pdisp.second));
|
displacement.set(0, -abs(pdisp.y));
|
||||||
break;
|
break;
|
||||||
case EAST:
|
case EAST:
|
||||||
displacement = std::make_pair(abs(pdisp.first), 0);
|
displacement.set(abs(pdisp.x), 0);
|
||||||
break;
|
break;
|
||||||
case SOUTH:
|
case SOUTH:
|
||||||
displacement = std::make_pair(0, abs(pdisp.second));
|
displacement.set(0, abs(pdisp.y));
|
||||||
break;
|
break;
|
||||||
case WEST:
|
case WEST:
|
||||||
displacement = std::make_pair(-abs(pdisp.first), 0);
|
displacement.set(-abs(pdisp.x), 0);
|
||||||
break;
|
break;
|
||||||
case NORTHEAST:
|
case NORTHEAST:
|
||||||
displacement = std::make_pair(abs(pdisp.first), -abs(pdisp.second));
|
displacement.set(abs(pdisp.x), -abs(pdisp.y));
|
||||||
break;
|
break;
|
||||||
case SOUTHEAST:
|
case SOUTHEAST:
|
||||||
displacement = std::make_pair(abs(pdisp.first), abs(pdisp.second));
|
displacement.set(abs(pdisp.x), abs(pdisp.y));
|
||||||
break;
|
break;
|
||||||
case NORTHWEST:
|
case NORTHWEST:
|
||||||
displacement = std::make_pair(-abs(pdisp.first), -abs(pdisp.second));
|
displacement.set(-abs(pdisp.x), -abs(pdisp.y));
|
||||||
break;
|
break;
|
||||||
case SOUTHWEST:
|
case SOUTHWEST:
|
||||||
displacement = std::make_pair(-abs(pdisp.first), abs(pdisp.second));
|
displacement.set(-abs(pdisp.x), abs(pdisp.y));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
MAPNIK_LOG_WARN(text_placements) << "Unknown placement";
|
MAPNIK_LOG_WARN(text_placements) << "Unknown placement";
|
||||||
|
|
|
@ -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
|
|
323
src/text/renderer.cpp
Normal file
323
src/text/renderer.cpp
Normal file
|
@ -0,0 +1,323 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
// mapnik
|
||||||
|
#include <mapnik/text/renderer.hpp>
|
||||||
|
#include <mapnik/graphics.hpp>
|
||||||
|
#include <mapnik/grid/grid.hpp>
|
||||||
|
#include <mapnik/text/text_properties.hpp>
|
||||||
|
#include <mapnik/font_engine_freetype.hpp>
|
||||||
|
#include <mapnik/text/face.hpp>
|
||||||
|
|
||||||
|
namespace mapnik
|
||||||
|
{
|
||||||
|
|
||||||
|
text_renderer::text_renderer (halo_rasterizer_e rasterizer, composite_mode_e comp_op, double scale_factor, stroker_ptr stroker)
|
||||||
|
: rasterizer_(rasterizer),
|
||||||
|
comp_op_(comp_op),
|
||||||
|
scale_factor_(scale_factor),
|
||||||
|
glyphs_(),
|
||||||
|
stroker_(stroker)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void text_renderer::prepare_glyphs(glyph_positions const& positions)
|
||||||
|
{
|
||||||
|
FT_Matrix matrix;
|
||||||
|
FT_Vector pen;
|
||||||
|
FT_Error error;
|
||||||
|
|
||||||
|
for (auto const& glyph_pos : positions)
|
||||||
|
{
|
||||||
|
glyph_info const& glyph = *(glyph_pos.glyph);
|
||||||
|
glyph.face->set_character_sizes(glyph.format->text_size * scale_factor_); //TODO: Optimize this?
|
||||||
|
|
||||||
|
matrix.xx = static_cast<FT_Fixed>( glyph_pos.rot.cos * 0x10000L);
|
||||||
|
matrix.xy = static_cast<FT_Fixed>(-glyph_pos.rot.sin * 0x10000L);
|
||||||
|
matrix.yx = static_cast<FT_Fixed>( glyph_pos.rot.sin * 0x10000L);
|
||||||
|
matrix.yy = static_cast<FT_Fixed>( glyph_pos.rot.cos * 0x10000L);
|
||||||
|
|
||||||
|
pixel_position pos = glyph_pos.pos + glyph.offset.rotate(glyph_pos.rot);
|
||||||
|
pen.x = static_cast<FT_Pos>(pos.x * 64);
|
||||||
|
pen.y = static_cast<FT_Pos>(pos.y * 64);
|
||||||
|
|
||||||
|
FT_Face face = glyph.face->get_face();
|
||||||
|
FT_Set_Transform(face, &matrix, &pen);
|
||||||
|
|
||||||
|
error = FT_Load_Glyph(face, glyph.glyph_index, FT_LOAD_NO_HINTING);
|
||||||
|
if (error) continue;
|
||||||
|
|
||||||
|
FT_Glyph image;
|
||||||
|
error = FT_Get_Glyph(face->glyph, &image);
|
||||||
|
if (error) continue;
|
||||||
|
|
||||||
|
glyphs_.emplace_back(image, glyph.format);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
unsigned gray=bitmap->buffer[q*bitmap->width+p];
|
||||||
|
if (gray)
|
||||||
|
{
|
||||||
|
pixmap.composite_pixel(comp_op, i, j, rgba, gray, opacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
agg_text_renderer<T>::agg_text_renderer (pixmap_type & pixmap,
|
||||||
|
halo_rasterizer_e rasterizer,
|
||||||
|
composite_mode_e comp_op,
|
||||||
|
double scale_factor,
|
||||||
|
stroker_ptr stroker)
|
||||||
|
: text_renderer(rasterizer, comp_op, scale_factor, stroker), pixmap_(pixmap)
|
||||||
|
{}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void agg_text_renderer<T>::render(glyph_positions const& pos)
|
||||||
|
{
|
||||||
|
glyphs_.clear();
|
||||||
|
prepare_glyphs(pos);
|
||||||
|
FT_Error error;
|
||||||
|
FT_Vector start;
|
||||||
|
int height = pixmap_.height();
|
||||||
|
pixel_position const& base_point = pos.get_base_point();
|
||||||
|
|
||||||
|
start.x = static_cast<FT_Pos>(base_point.x * (1 << 6));
|
||||||
|
start.y = static_cast<FT_Pos>((height - base_point.y) * (1 << 6));
|
||||||
|
|
||||||
|
//render halo
|
||||||
|
double halo_radius = 0;
|
||||||
|
char_properties_ptr format;
|
||||||
|
for (auto const& glyph : glyphs_)
|
||||||
|
{
|
||||||
|
if (glyph.properties)
|
||||||
|
{
|
||||||
|
format = glyph.properties;
|
||||||
|
// Settings have changed.
|
||||||
|
halo_radius = glyph.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(glyph.image, &g);
|
||||||
|
if (!error)
|
||||||
|
{
|
||||||
|
FT_Glyph_Transform(g,0,&start);
|
||||||
|
if (rasterizer_ == HALO_RASTERIZER_FULL)
|
||||||
|
{
|
||||||
|
stroker_->init(halo_radius);
|
||||||
|
FT_Glyph_Stroke(&g, stroker_->get(), 1);
|
||||||
|
error = FT_Glyph_To_Bitmap(&g, FT_RENDER_MODE_NORMAL, 0, 1);
|
||||||
|
if (!error)
|
||||||
|
{
|
||||||
|
FT_BitmapGlyph bit = reinterpret_cast<FT_BitmapGlyph>(g);
|
||||||
|
composite_bitmap(pixmap_,
|
||||||
|
&bit->bitmap,
|
||||||
|
format->halo_fill.rgba(),
|
||||||
|
bit->left,
|
||||||
|
height - bit->top,
|
||||||
|
format->text_opacity,
|
||||||
|
comp_op_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
error = FT_Glyph_To_Bitmap(&g, FT_RENDER_MODE_NORMAL, 0, 1);
|
||||||
|
if (!error)
|
||||||
|
{
|
||||||
|
FT_BitmapGlyph bit = reinterpret_cast<FT_BitmapGlyph>(g);
|
||||||
|
render_halo(&bit->bitmap,
|
||||||
|
format->halo_fill.rgba(),
|
||||||
|
bit->left,
|
||||||
|
height - bit->top,
|
||||||
|
halo_radius,
|
||||||
|
format->text_opacity,
|
||||||
|
comp_op_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FT_Done_Glyph(g);
|
||||||
|
}
|
||||||
|
|
||||||
|
// render actual text
|
||||||
|
for (auto & glyph : glyphs_)
|
||||||
|
{
|
||||||
|
if (glyph.properties)
|
||||||
|
{
|
||||||
|
format = glyph.properties;
|
||||||
|
}
|
||||||
|
FT_Glyph_Transform(glyph.image, 0, &start);
|
||||||
|
error = FT_Glyph_To_Bitmap(&glyph.image ,FT_RENDER_MODE_NORMAL,0,1);
|
||||||
|
if (!error)
|
||||||
|
{
|
||||||
|
FT_BitmapGlyph bit = reinterpret_cast<FT_BitmapGlyph>(glyph.image);
|
||||||
|
composite_bitmap(pixmap_,
|
||||||
|
&bit->bitmap,
|
||||||
|
format->fill.rgba(),
|
||||||
|
bit->left,
|
||||||
|
height - bit->top,
|
||||||
|
format->text_opacity,
|
||||||
|
comp_op_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void grid_text_renderer<T>::render(glyph_positions const& pos, value_integer feature_id)
|
||||||
|
{
|
||||||
|
glyphs_.clear();
|
||||||
|
prepare_glyphs(pos);
|
||||||
|
FT_Error error;
|
||||||
|
FT_Vector start;
|
||||||
|
unsigned height = pixmap_.height();
|
||||||
|
pixel_position const& base_point = pos.get_base_point();
|
||||||
|
start.x = static_cast<FT_Pos>(base_point.x * (1 << 6));
|
||||||
|
start.y = static_cast<FT_Pos>((height - base_point.y) * (1 << 6));
|
||||||
|
|
||||||
|
// now render transformed glyphs
|
||||||
|
double halo_radius = 0.0;
|
||||||
|
for (auto & glyph : glyphs_)
|
||||||
|
{
|
||||||
|
if (glyph.properties)
|
||||||
|
{
|
||||||
|
halo_radius = glyph.properties->halo_radius * scale_factor_;
|
||||||
|
}
|
||||||
|
FT_Glyph_Transform(glyph.image, 0, &start);
|
||||||
|
error = FT_Glyph_To_Bitmap(&glyph.image, FT_RENDER_MODE_NORMAL, 0, 1);
|
||||||
|
if (!error)
|
||||||
|
{
|
||||||
|
|
||||||
|
FT_BitmapGlyph bit = reinterpret_cast<FT_BitmapGlyph>(glyph.image);
|
||||||
|
render_halo_id(&bit->bitmap,
|
||||||
|
feature_id,
|
||||||
|
bit->left,
|
||||||
|
height - bit->top,
|
||||||
|
static_cast<int>(halo_radius));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void agg_text_renderer<T>::render_halo(FT_Bitmap *bitmap,
|
||||||
|
unsigned rgba,
|
||||||
|
int x1,
|
||||||
|
int y1,
|
||||||
|
double halo_radius,
|
||||||
|
double opacity,
|
||||||
|
composite_mode_e comp_op)
|
||||||
|
{
|
||||||
|
int width = bitmap->width;
|
||||||
|
int height = bitmap->rows;
|
||||||
|
int x, y;
|
||||||
|
if (halo_radius < 1.0)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void grid_text_renderer<T>::render_halo_id(
|
||||||
|
FT_Bitmap *bitmap,
|
||||||
|
mapnik::value_integer feature_id,
|
||||||
|
int x1,
|
||||||
|
int y1,
|
||||||
|
int halo_radius)
|
||||||
|
{
|
||||||
|
int width = bitmap->width;
|
||||||
|
int height = bitmap->rows;
|
||||||
|
int x, y;
|
||||||
|
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_.setPixel(x+x1+m,y+y1+n,feature_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
grid_text_renderer<T>::grid_text_renderer(pixmap_type &pixmap,
|
||||||
|
composite_mode_e comp_op,
|
||||||
|
double scale_factor) :
|
||||||
|
text_renderer(HALO_RASTERIZER_FAST, comp_op, scale_factor), pixmap_(pixmap)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template class agg_text_renderer<image_32>;
|
||||||
|
template class grid_text_renderer<grid>;
|
||||||
|
}
|
205
src/text/scrptrun.cpp
Normal file
205
src/text/scrptrun.cpp
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
/*
|
||||||
|
*******************************************************************************
|
||||||
|
*
|
||||||
|
* Copyright (C) 1999-2001, International Business Machines
|
||||||
|
* Corporation and others. All Rights Reserved.
|
||||||
|
*
|
||||||
|
*******************************************************************************
|
||||||
|
* file name: scrptrun.cpp
|
||||||
|
*
|
||||||
|
* created on: 10/17/2001
|
||||||
|
* created by: Eric R. Mader
|
||||||
|
*
|
||||||
|
* NOTE: This file is copied from ICU.
|
||||||
|
* http://source.icu-project.org/repos/icu/icu/trunk/license.html
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <unicode/utypes.h>
|
||||||
|
#include <unicode/uscript.h>
|
||||||
|
|
||||||
|
#include <mapnik/text/scrptrun.hpp>
|
||||||
|
|
||||||
|
#define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
|
||||||
|
|
||||||
|
const char ScriptRun::fgClassID=0;
|
||||||
|
|
||||||
|
UChar32 ScriptRun::pairedChars[] = {
|
||||||
|
0x0028, 0x0029, // ascii paired punctuation
|
||||||
|
0x003c, 0x003e,
|
||||||
|
0x005b, 0x005d,
|
||||||
|
0x007b, 0x007d,
|
||||||
|
0x00ab, 0x00bb, // guillemets
|
||||||
|
0x2018, 0x2019, // general punctuation
|
||||||
|
0x201c, 0x201d,
|
||||||
|
0x2039, 0x203a,
|
||||||
|
0x3008, 0x3009, // chinese paired punctuation
|
||||||
|
0x300a, 0x300b,
|
||||||
|
0x300c, 0x300d,
|
||||||
|
0x300e, 0x300f,
|
||||||
|
0x3010, 0x3011,
|
||||||
|
0x3014, 0x3015,
|
||||||
|
0x3016, 0x3017,
|
||||||
|
0x3018, 0x3019,
|
||||||
|
0x301a, 0x301b
|
||||||
|
};
|
||||||
|
|
||||||
|
const int32_t ScriptRun::pairedCharCount = ARRAY_SIZE(pairedChars);
|
||||||
|
const int32_t ScriptRun::pairedCharPower = 1 << highBit(pairedCharCount);
|
||||||
|
const int32_t ScriptRun::pairedCharExtra = pairedCharCount - pairedCharPower;
|
||||||
|
|
||||||
|
int8_t ScriptRun::highBit(int32_t value)
|
||||||
|
{
|
||||||
|
if (value <= 0) {
|
||||||
|
return -32;
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t bit = 0;
|
||||||
|
|
||||||
|
if (value >= 1 << 16) {
|
||||||
|
value >>= 16;
|
||||||
|
bit += 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value >= 1 << 8) {
|
||||||
|
value >>= 8;
|
||||||
|
bit += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value >= 1 << 4) {
|
||||||
|
value >>= 4;
|
||||||
|
bit += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value >= 1 << 2) {
|
||||||
|
value >>= 2;
|
||||||
|
bit += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value >= 1 << 1) {
|
||||||
|
value >>= 1;
|
||||||
|
bit += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t ScriptRun::getPairIndex(UChar32 ch)
|
||||||
|
{
|
||||||
|
int32_t probe = pairedCharPower;
|
||||||
|
int32_t index = 0;
|
||||||
|
|
||||||
|
if (ch >= pairedChars[pairedCharExtra]) {
|
||||||
|
index = pairedCharExtra;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (probe > (1 << 0)) {
|
||||||
|
probe >>= 1;
|
||||||
|
|
||||||
|
if (ch >= pairedChars[index + probe]) {
|
||||||
|
index += probe;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pairedChars[index] != ch) {
|
||||||
|
index = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
UBool ScriptRun::sameScript(int32_t scriptOne, int32_t scriptTwo)
|
||||||
|
{
|
||||||
|
return scriptOne <= USCRIPT_INHERITED || scriptTwo <= USCRIPT_INHERITED || scriptOne == scriptTwo;
|
||||||
|
}
|
||||||
|
|
||||||
|
UBool ScriptRun::next()
|
||||||
|
{
|
||||||
|
int32_t startSP = parenSP; // used to find the first new open character
|
||||||
|
UErrorCode error = U_ZERO_ERROR;
|
||||||
|
|
||||||
|
// if we've fallen off the end of the text, we're done
|
||||||
|
if (scriptEnd >= charLimit) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
scriptCode = USCRIPT_COMMON;
|
||||||
|
|
||||||
|
for (scriptStart = scriptEnd; scriptEnd < charLimit; scriptEnd += 1) {
|
||||||
|
UChar high = charArray[scriptEnd];
|
||||||
|
UChar32 ch = high;
|
||||||
|
|
||||||
|
// if the character is a high surrogate and it's not the last one
|
||||||
|
// in the text, see if it's followed by a low surrogate
|
||||||
|
if (high >= 0xD800 && high <= 0xDBFF && scriptEnd < charLimit - 1)
|
||||||
|
{
|
||||||
|
UChar low = charArray[scriptEnd + 1];
|
||||||
|
|
||||||
|
// if it is followed by a low surrogate,
|
||||||
|
// consume it and form the full character
|
||||||
|
if (low >= 0xDC00 && low <= 0xDFFF) {
|
||||||
|
ch = (high - 0xD800) * 0x0400 + low - 0xDC00 + 0x10000;
|
||||||
|
scriptEnd += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UScriptCode sc = uscript_getScript(ch, &error);
|
||||||
|
int32_t pairIndex = getPairIndex(ch);
|
||||||
|
|
||||||
|
// Paired character handling:
|
||||||
|
//
|
||||||
|
// if it's an open character, push it onto the stack.
|
||||||
|
// if it's a close character, find the matching open on the
|
||||||
|
// stack, and use that script code. Any non-matching open
|
||||||
|
// characters above it on the stack will be poped.
|
||||||
|
if (pairIndex >= 0) {
|
||||||
|
if ((pairIndex & 1) == 0) {
|
||||||
|
parenStack[++parenSP].pairIndex = pairIndex;
|
||||||
|
parenStack[parenSP].scriptCode = scriptCode;
|
||||||
|
} else if (parenSP >= 0) {
|
||||||
|
int32_t pi = pairIndex & ~1;
|
||||||
|
|
||||||
|
while (parenSP >= 0 && parenStack[parenSP].pairIndex != pi) {
|
||||||
|
parenSP -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parenSP < startSP) {
|
||||||
|
startSP = parenSP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parenSP >= 0) {
|
||||||
|
sc = parenStack[parenSP].scriptCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sameScript(scriptCode, sc)) {
|
||||||
|
if (scriptCode <= USCRIPT_INHERITED && sc > USCRIPT_INHERITED) {
|
||||||
|
scriptCode = sc;
|
||||||
|
|
||||||
|
// now that we have a final script code, fix any open
|
||||||
|
// characters we pushed before we knew the script code.
|
||||||
|
while (startSP < parenSP) {
|
||||||
|
parenStack[++startSP].scriptCode = scriptCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if this character is a close paired character,
|
||||||
|
// pop it from the stack
|
||||||
|
if (pairIndex >= 0 && (pairIndex & 1) != 0 && parenSP >= 0) {
|
||||||
|
parenSP -= 1;
|
||||||
|
startSP -= 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if the run broke on a surrogate pair,
|
||||||
|
// end it before the high surrogate
|
||||||
|
if (ch >= 0x10000) {
|
||||||
|
scriptEnd -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
*
|
*
|
||||||
* This file is part of Mapnik (c++ mapping toolkit)
|
* 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
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
@ -21,120 +21,58 @@
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
// mapnik
|
// 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/text/symbolizer_helpers.hpp>
|
||||||
#include <mapnik/label_collision_detector.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/geom_util.hpp>
|
||||||
#include <mapnik/marker.hpp>
|
#include <mapnik/parse_path.hpp>
|
||||||
#include <mapnik/expression_evaluator.hpp>
|
#include <mapnik/debug.hpp>
|
||||||
#include <mapnik/pixel_position.hpp>
|
|
||||||
|
|
||||||
// agg
|
//agg
|
||||||
#include "agg_conv_clip_polyline.h"
|
#include "agg_conv_clip_polyline.h"
|
||||||
|
|
||||||
namespace mapnik {
|
namespace mapnik {
|
||||||
|
|
||||||
template <typename FaceManagerT, typename DetectorT>
|
template <typename FaceManagerT, typename DetectorT>
|
||||||
text_symbolizer_helper<FaceManagerT, DetectorT>::text_symbolizer_helper(text_symbolizer const& sym,
|
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)
|
||||||
feature_impl const& feature,
|
: sym_(sym),
|
||||||
proj_transform const& prj_trans,
|
feature_(feature),
|
||||||
unsigned width,
|
prj_trans_(prj_trans),
|
||||||
unsigned height,
|
t_(t),
|
||||||
double scale_factor,
|
dims_(0, 0, width, height),
|
||||||
CoordTransform const& t,
|
query_extent_(query_extent),
|
||||||
FaceManagerT &font_manager,
|
points_on_line_(false),
|
||||||
DetectorT &detector,
|
placement_(sym_.get_placement_options()->get_placement_info(scale_factor)),
|
||||||
box2d<double> const& query_extent)
|
finder_(feature, detector, dims_, placement_, font_manager, scale_factor)
|
||||||
: sym_(sym),
|
{
|
||||||
feature_(feature),
|
initialize_geometries();
|
||||||
prj_trans_(prj_trans),
|
if (!geometries_to_process_.size()) return;
|
||||||
t_(t),
|
finder_.next_position();
|
||||||
font_manager_(font_manager),
|
initialize_points();
|
||||||
detector_(detector),
|
}
|
||||||
dims_(0, 0, width, height),
|
|
||||||
query_extent_(query_extent),
|
placements_list const& text_symbolizer_helper::get()
|
||||||
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()
|
|
||||||
{
|
{
|
||||||
if (!placement_valid_) return false;
|
|
||||||
if (point_placement_)
|
if (point_placement_)
|
||||||
return next_point_placement();
|
|
||||||
else if (sym_.clip())
|
|
||||||
return next_line_placement_clipped();
|
|
||||||
else
|
|
||||||
return next_line_placement();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename FaceManagerT, typename DetectorT>
|
|
||||||
bool text_symbolizer_helper<FaceManagerT, DetectorT>::next_line_placement()
|
|
||||||
{
|
|
||||||
while (!geometries_to_process_.empty())
|
|
||||||
{
|
{
|
||||||
if (geo_itr_ == geometries_to_process_.end())
|
while (next_point_placement());
|
||||||
{
|
|
||||||
//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_++;
|
|
||||||
}
|
}
|
||||||
return false;
|
else
|
||||||
|
{
|
||||||
|
while (next_line_placement());
|
||||||
|
}
|
||||||
|
return finder_.placements();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FaceManagerT, typename DetectorT>
|
bool text_symbolizer_helper::next_line_placement()
|
||||||
bool text_symbolizer_helper<FaceManagerT, DetectorT>::next_line_placement_clipped()
|
|
||||||
{
|
{
|
||||||
while (!geometries_to_process_.empty())
|
while (!geometries_to_process_.empty())
|
||||||
{
|
{
|
||||||
if (geo_itr_ == geometries_to_process_.end())
|
if (geo_itr_ == geometries_to_process_.end())
|
||||||
{
|
{
|
||||||
//Just processed the last geometry. Try next placement.
|
//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
|
//Start again from begin of list
|
||||||
geo_itr_ = geometries_to_process_.begin();
|
geo_itr_ = geometries_to_process_.begin();
|
||||||
continue; //Reexecute size check
|
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 agg::conv_clip_polyline<geometry_type> clipped_geometry_type;
|
||||||
typedef coord_transform<CoordTransform,clipped_geometry_type> path_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());
|
|
||||||
path_type path(t_, clipped, prj_trans_);
|
|
||||||
|
|
||||||
finder_->clear_placements();
|
clipped_geometry_type clipped(**geo_itr_);
|
||||||
if (points_on_line_) {
|
clipped.clip_box(query_extent_.minx(), query_extent_.miny(),
|
||||||
finder_->find_point_placements(path);
|
query_extent_.maxx(), query_extent_.maxy());
|
||||||
} else {
|
path_type path(t_, clipped, prj_trans_);
|
||||||
finder_->find_line_placements(path);
|
bool success = finder_.find_line_placements(path, points_on_line_);
|
||||||
}
|
if (success)
|
||||||
if (!finder_->get_results().empty())
|
|
||||||
{
|
{
|
||||||
//Found a placement
|
//Found a placement
|
||||||
if (points_on_line_)
|
|
||||||
{
|
|
||||||
finder_->update_detector();
|
|
||||||
}
|
|
||||||
geo_itr_ = geometries_to_process_.erase(geo_itr_);
|
geo_itr_ = geometries_to_process_.erase(geo_itr_);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -168,26 +98,22 @@ bool text_symbolizer_helper<FaceManagerT, DetectorT>::next_line_placement_clippe
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FaceManagerT, typename DetectorT>
|
bool text_symbolizer_helper::next_point_placement()
|
||||||
bool text_symbolizer_helper<FaceManagerT, DetectorT>::next_point_placement()
|
|
||||||
{
|
{
|
||||||
while (!points_.empty())
|
while (!points_.empty())
|
||||||
{
|
{
|
||||||
if (point_itr_ == points_.end())
|
if (point_itr_ == points_.end())
|
||||||
{
|
{
|
||||||
//Just processed the last point. Try next placement.
|
//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
|
//Start again from begin of list
|
||||||
point_itr_ = points_.begin();
|
point_itr_ = points_.begin();
|
||||||
continue; //Reexecute size check
|
continue; //Reexecute size check
|
||||||
}
|
}
|
||||||
finder_->clear_placements();
|
if (finder_.find_point_placement(*point_itr_))
|
||||||
finder_->find_point_placement(point_itr_->first, point_itr_->second, angle_);
|
|
||||||
if (!finder_->get_results().empty())
|
|
||||||
{
|
{
|
||||||
//Found a placement
|
//Found a placement
|
||||||
point_itr_ = points_.erase(point_itr_);
|
point_itr_ = points_.erase(point_itr_);
|
||||||
finder_->update_detector();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
//No placement for this point. Keep it in points_ for next try.
|
//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::initialize_geometries()
|
||||||
void text_symbolizer_helper<FaceManagerT, DetectorT>::initialize_geometries()
|
|
||||||
{
|
{
|
||||||
bool largest_box_only = false;
|
bool largest_box_only = false;
|
||||||
std::size_t num_geom = feature_.num_geometries();
|
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);
|
geometry_type const& geom = feature_.get_geometry(i);
|
||||||
|
|
||||||
// don't bother with empty geometries
|
// don't bother with empty geometries
|
||||||
if (geom.size() == 0) continue;
|
if (geom.size() == 0) continue;
|
||||||
geometry_type::types type = geom.type();
|
mapnik::geometry_type::types type = geom.type();
|
||||||
if (type == geometry_type::types::Polygon)
|
if (type == geometry_type::types::Polygon)
|
||||||
{
|
{
|
||||||
largest_box_only = sym_.largest_bbox_only();
|
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();
|
geo_itr_ = geometries_to_process_.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FaceManagerT, typename DetectorT>
|
void text_symbolizer_helper::initialize_points()
|
||||||
void text_symbolizer_helper<FaceManagerT, DetectorT>::initialize_points()
|
|
||||||
{
|
{
|
||||||
label_placement_enum how_placed = placement_->properties.label_placement;
|
label_placement_enum how_placed = placement_->properties.label_placement;
|
||||||
if (how_placed == LINE_PLACEMENT)
|
if (how_placed == LINE_PLACEMENT)
|
||||||
|
@ -276,7 +199,7 @@ void text_symbolizer_helper<FaceManagerT, DetectorT>::initialize_points()
|
||||||
geom.vertex(&label_x, &label_y);
|
geom.vertex(&label_x, &label_y);
|
||||||
prj_trans_.backward(label_x, label_y, z);
|
prj_trans_.backward(label_x, label_y, z);
|
||||||
t_.forward(&label_x, &label_y);
|
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
|
else
|
||||||
|
@ -304,205 +227,83 @@ void text_symbolizer_helper<FaceManagerT, DetectorT>::initialize_points()
|
||||||
{
|
{
|
||||||
prj_trans_.backward(label_x, label_y, z);
|
prj_trans_.backward(label_x, label_y, z);
|
||||||
t_.forward(&label_x, &label_y);
|
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();
|
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>
|
template <typename FaceManagerT, typename DetectorT>
|
||||||
bool shield_symbolizer_helper<FaceManagerT, DetectorT>::next()
|
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)
|
||||||
{
|
{
|
||||||
if (!placement_valid_ || !marker_) return false;
|
initialize_geometries();
|
||||||
if (point_placement_)
|
if (!geometries_to_process_.size()) return;
|
||||||
return next_point_placement();
|
finder_.next_position();
|
||||||
else
|
initialize_points();
|
||||||
return next_line_placement();
|
init_marker();
|
||||||
}
|
|
||||||
|
|
||||||
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>
|
void text_symbolizer_helper::init_marker()
|
||||||
bool shield_symbolizer_helper<FaceManagerT, DetectorT>::next_line_placement()
|
|
||||||
{
|
{
|
||||||
position const& pos = placement_->properties.displacement;
|
shield_symbolizer const& sym = static_cast<shield_symbolizer const&>(sym_);
|
||||||
finder_->additional_boxes().clear();
|
std::string filename = path_processor_type::evaluate(*sym.get_filename(), feature_);
|
||||||
//Markers are automatically centered
|
agg::trans_affine trans;
|
||||||
finder_->additional_boxes().push_back(
|
evaluate_transform(trans, feature_, sym.get_image_transform());
|
||||||
box2d<double>(-0.5 * marker_ext_.width() - pos.first,
|
boost::optional<marker_ptr> opt_marker; //TODO: Why boost::optional?
|
||||||
-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();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template <typename FaceManagerT, typename DetectorT>
|
|
||||||
void shield_symbolizer_helper<FaceManagerT, DetectorT>::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();
|
|
||||||
if (!filename.empty())
|
if (!filename.empty())
|
||||||
{
|
{
|
||||||
marker_ = marker_cache::instance().find(filename, true);
|
opt_marker = marker_cache::instance().find(filename, true);
|
||||||
}
|
}
|
||||||
if (!marker_) {
|
marker_ptr m;
|
||||||
marker_w_ = 0;
|
if (opt_marker) m = *opt_marker;
|
||||||
marker_h_ = 0;
|
if (!m) return;
|
||||||
marker_ext_.init(0, 0, 0, 0);
|
double width = m->width();
|
||||||
return;
|
double height = m->height();
|
||||||
}
|
double px0 = - 0.5 * width;
|
||||||
marker_w_ = (*marker_)->width();
|
double py0 = - 0.5 * height;
|
||||||
marker_h_ = (*marker_)->height();
|
double px1 = 0.5 * width;
|
||||||
double px0 = - 0.5 * marker_w_;
|
double py1 = 0.5 * height;
|
||||||
double py0 = - 0.5 * marker_h_;
|
|
||||||
double px1 = 0.5 * marker_w_;
|
|
||||||
double py1 = 0.5 * marker_h_;
|
|
||||||
double px2 = px1;
|
double px2 = px1;
|
||||||
double py2 = py0;
|
double py2 = py0;
|
||||||
double px3 = px0;
|
double px3 = px0;
|
||||||
double py3 = py1;
|
double py3 = py1;
|
||||||
image_transform_.transform(&px0,&py0);
|
trans.transform(&px0, &py0);
|
||||||
image_transform_.transform(&px1,&py1);
|
trans.transform(&px1, &py1);
|
||||||
image_transform_.transform(&px2,&py2);
|
trans.transform(&px2, &py2);
|
||||||
image_transform_.transform(&px3,&py3);
|
trans.transform(&px3, &py3);
|
||||||
marker_ext_.init(px0, py0, px1, py1);
|
box2d<double> bbox(px0, py0, px1, py1);
|
||||||
marker_ext_.expand_to_include(px2, py2);
|
bbox.expand_to_include(px2, py2);
|
||||||
marker_ext_.expand_to_include(px3, py3);
|
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 <typename FaceManagerT, typename DetectorT>
|
template text_symbolizer_helper::text_symbolizer_helper(const text_symbolizer &sym, const feature_impl &feature,
|
||||||
pixel_position shield_symbolizer_helper<FaceManagerT, DetectorT>::get_marker_position(text_path const& p)
|
const proj_transform &prj_trans,
|
||||||
{
|
unsigned width, unsigned height, double scale_factor,
|
||||||
position const& pos = placement_->properties.displacement;
|
const CoordTransform &t, face_manager<freetype_engine> &font_manager,
|
||||||
if (placement_->properties.label_placement == LINE_PLACEMENT) {
|
label_collision_detector4 &detector, const box2d<double> &query_extent);
|
||||||
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 text_symbolizer_helper::text_symbolizer_helper(const shield_symbolizer &sym, const feature_impl &feature,
|
||||||
template <typename FaceManagerT, typename DetectorT>
|
const proj_transform &prj_trans,
|
||||||
marker& shield_symbolizer_helper<FaceManagerT, DetectorT>::get_marker() const
|
unsigned width, unsigned height, double scale_factor,
|
||||||
{
|
const CoordTransform &t, face_manager<freetype_engine> &font_manager,
|
||||||
return **marker_;
|
label_collision_detector4 &detector, const box2d<double> &query_extent);
|
||||||
}
|
|
||||||
|
|
||||||
template <typename FaceManagerT, typename DetectorT>
|
|
||||||
agg::trans_affine const& shield_symbolizer_helper<FaceManagerT, DetectorT>::get_image_transform() const
|
|
||||||
{
|
|
||||||
return image_transform_;
|
|
||||||
}
|
|
||||||
|
|
||||||
template class text_symbolizer_helper<face_manager<freetype_engine>, label_collision_detector4>;
|
|
||||||
template class shield_symbolizer_helper<face_manager<freetype_engine>, label_collision_detector4>;
|
|
||||||
} //namespace
|
} //namespace
|
||||||
|
|
99
src/text/text_line.cpp
Normal file
99
src/text/text_line.cpp
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* 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/text_line.hpp>
|
||||||
|
|
||||||
|
namespace mapnik {
|
||||||
|
|
||||||
|
text_line::text_line(unsigned first_char, unsigned last_char)
|
||||||
|
: glyphs_(),
|
||||||
|
line_height_(0.0),
|
||||||
|
max_char_height_(0.0),
|
||||||
|
width_(0.0),
|
||||||
|
first_char_(first_char),
|
||||||
|
last_char_(last_char),
|
||||||
|
first_line_(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void text_line::add_glyph(glyph_info const& glyph, double scale_factor_)
|
||||||
|
{
|
||||||
|
line_height_ = std::max(line_height_, glyph.line_height + glyph.format->line_spacing);
|
||||||
|
if (glyphs_.empty())
|
||||||
|
{
|
||||||
|
width_ = glyph.width;
|
||||||
|
}
|
||||||
|
else if (glyph.width)
|
||||||
|
{
|
||||||
|
// Only add character spacing if the character is not a zero-width part of a cluster.
|
||||||
|
width_ += glyph.width + glyphs_.back().format->character_spacing * scale_factor_;
|
||||||
|
}
|
||||||
|
glyphs_.push_back(glyph);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void text_line::reserve(glyph_vector::size_type length)
|
||||||
|
{
|
||||||
|
glyphs_.reserve(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
text_line::const_iterator text_line::begin() const
|
||||||
|
{
|
||||||
|
return glyphs_.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
text_line::const_iterator text_line::end() const
|
||||||
|
{
|
||||||
|
return glyphs_.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
double text_line::height() const
|
||||||
|
{
|
||||||
|
if (first_line_) return max_char_height_;
|
||||||
|
return line_height_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void text_line::update_max_char_height(double max_char_height)
|
||||||
|
{
|
||||||
|
max_char_height_ = std::max(max_char_height_, max_char_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void text_line::set_first_line(bool first_line)
|
||||||
|
{
|
||||||
|
first_line_ = first_line;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned text_line::first_char() const
|
||||||
|
{
|
||||||
|
return first_char_;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned text_line::last_char() const
|
||||||
|
{
|
||||||
|
return last_char_;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned text_line::size() const
|
||||||
|
{
|
||||||
|
return glyphs_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace mapnik
|
|
@ -2,7 +2,7 @@
|
||||||
*
|
*
|
||||||
* This file is part of Mapnik (c++ mapping toolkit)
|
* 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
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
@ -20,10 +20,10 @@
|
||||||
*
|
*
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
// mapnik
|
// mapnik
|
||||||
|
#include <mapnik/text/text_properties.hpp>
|
||||||
|
#include <mapnik/text/layout.hpp>
|
||||||
#include <mapnik/debug.hpp>
|
#include <mapnik/debug.hpp>
|
||||||
#include <mapnik/feature.hpp>
|
#include <mapnik/feature.hpp>
|
||||||
#include <mapnik/text/text_properties.hpp>
|
|
||||||
#include <mapnik/text/processed_text.hpp>
|
|
||||||
#include <mapnik/ptree_helpers.hpp>
|
#include <mapnik/ptree_helpers.hpp>
|
||||||
#include <mapnik/expression_string.hpp>
|
#include <mapnik/expression_string.hpp>
|
||||||
#include <mapnik/text/formatting/text.hpp>
|
#include <mapnik/text/formatting/text.hpp>
|
||||||
|
@ -31,12 +31,71 @@
|
||||||
#include <mapnik/config_error.hpp>
|
#include <mapnik/config_error.hpp>
|
||||||
|
|
||||||
// boost
|
// boost
|
||||||
|
|
||||||
#include <boost/property_tree/ptree.hpp>
|
#include <boost/property_tree/ptree.hpp>
|
||||||
|
|
||||||
namespace mapnik
|
namespace mapnik
|
||||||
{
|
{
|
||||||
using boost::optional;
|
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() :
|
text_symbolizer_properties::text_symbolizer_properties() :
|
||||||
orientation(),
|
orientation(),
|
||||||
displacement(0.0,0.0),
|
displacement(0.0,0.0),
|
||||||
|
@ -56,13 +115,16 @@ text_symbolizer_properties::text_symbolizer_properties() :
|
||||||
largest_bbox_only(true),
|
largest_bbox_only(true),
|
||||||
text_ratio(0.0),
|
text_ratio(0.0),
|
||||||
wrap_width(0.0),
|
wrap_width(0.0),
|
||||||
format(),
|
wrap_before(false),
|
||||||
|
rotate_displacement(false),
|
||||||
|
upright(UPRIGHT_AUTO),
|
||||||
|
format(std::make_shared<char_properties>()),
|
||||||
tree_()
|
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();
|
output.clear();
|
||||||
if (tree_) {
|
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_;
|
if (text_ratio_) text_ratio = *text_ratio_;
|
||||||
optional<double> wrap_width_ = sym.get_opt_attr<double>("wrap-width");
|
optional<double> wrap_width_ = sym.get_opt_attr<double>("wrap-width");
|
||||||
if (wrap_width_) wrap_width = *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");
|
optional<double> label_position_tolerance_ = sym.get_opt_attr<double>("label-position-tolerance");
|
||||||
if (label_position_tolerance_) label_position_tolerance = *label_position_tolerance_;
|
if (label_position_tolerance_) label_position_tolerance = *label_position_tolerance_;
|
||||||
optional<double> spacing_ = sym.get_opt_attr<double>("spacing");
|
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_;
|
if (jalign_) jalign = *jalign_;
|
||||||
optional<expression_ptr> orientation_ = sym.get_opt_attr<expression_ptr>("orientation");
|
optional<expression_ptr> orientation_ = sym.get_opt_attr<expression_ptr>("orientation");
|
||||||
if (orientation_) orientation = *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");
|
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");
|
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");
|
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);
|
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_);
|
set_old_style_expression(*name_);
|
||||||
}
|
}
|
||||||
|
|
||||||
format.from_xml(sym, fontsets);
|
format->from_xml(sym, fontsets);
|
||||||
formatting::node_ptr n(formatting::node::from_xml(sym));
|
formatting::node_ptr n(formatting::node::from_xml(sym));
|
||||||
if (n) set_format_tree(n);
|
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)
|
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);
|
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)
|
if (label_position_tolerance != dfl.label_position_tolerance || explicit_defaults)
|
||||||
{
|
{
|
||||||
set_attr(node, "label-position-tolerance", label_position_tolerance);
|
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);
|
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);
|
if (tree_) tree_->to_xml(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,7 +326,6 @@ char_properties::char_properties() :
|
||||||
character_spacing(0),
|
character_spacing(0),
|
||||||
line_spacing(0),
|
line_spacing(0),
|
||||||
text_opacity(1.0),
|
text_opacity(1.0),
|
||||||
wrap_before(false),
|
|
||||||
wrap_char(' '),
|
wrap_char(' '),
|
||||||
text_transform(NONE),
|
text_transform(NONE),
|
||||||
fill(color(0,0,0)),
|
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_;
|
if (halo_fill_) halo_fill = *halo_fill_;
|
||||||
optional<double> halo_radius_ = sym.get_opt_attr<double>("halo-radius");
|
optional<double> halo_radius_ = sym.get_opt_attr<double>("halo-radius");
|
||||||
if (halo_radius_) halo_radius = *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");
|
optional<text_transform_e> tconvert_ = sym.get_opt_attr<text_transform_e>("text-transform");
|
||||||
if (tconvert_) text_transform = *tconvert_;
|
if (tconvert_) text_transform = *tconvert_;
|
||||||
optional<double> line_spacing_ = sym.get_opt_attr<double>("line-spacing");
|
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);
|
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)
|
if (wrap_char != dfl.wrap_char || explicit_defaults)
|
||||||
{
|
{
|
||||||
set_attr(node, "wrap-character", std::string(1, wrap_char));
|
set_attr(node, "wrap-character", std::string(1, wrap_char));
|
||||||
|
|
222
src/text/vertex_cache.cpp
Normal file
222
src/text/vertex_cache.cpp
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
// mapnik
|
||||||
|
#include <mapnik/text/vertex_cache.hpp>
|
||||||
|
#include <mapnik/offset_converter.hpp>
|
||||||
|
|
||||||
|
// boost
|
||||||
|
|
||||||
|
|
||||||
|
namespace mapnik
|
||||||
|
{
|
||||||
|
|
||||||
|
double vertex_cache::angle(double width)
|
||||||
|
{
|
||||||
|
/* IMPORTANT NOTE: See note about coordinate systems in placement_finder::find_point_placement()
|
||||||
|
* for imformation about why the y axis is inverted! */
|
||||||
|
double tmp = width + position_in_segment_;
|
||||||
|
if ((tmp <= current_segment_->length) && (tmp >= 0))
|
||||||
|
{
|
||||||
|
//Only calculate angle on request as it is expensive
|
||||||
|
if (!angle_valid_)
|
||||||
|
{
|
||||||
|
angle_ = atan2(-(current_segment_->pos.y - segment_starting_point_.y),
|
||||||
|
current_segment_->pos.x - segment_starting_point_.x);
|
||||||
|
}
|
||||||
|
return width >= 0 ? angle_ : angle_ + M_PI;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
scoped_state s(*this);
|
||||||
|
pixel_position const& old_pos = s.get_state().position();
|
||||||
|
move(width);
|
||||||
|
double angle = atan2(-(current_position_.y - old_pos.y),
|
||||||
|
current_position_.x - old_pos.x);
|
||||||
|
return angle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool vertex_cache::next_subpath()
|
||||||
|
{
|
||||||
|
if (!initialized_)
|
||||||
|
{
|
||||||
|
current_subpath_ = subpaths_.begin();
|
||||||
|
initialized_ = true;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
current_subpath_++;
|
||||||
|
}
|
||||||
|
if (current_subpath_ == subpaths_.end()) return false;
|
||||||
|
rewind_subpath(); //Initialize position values
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vertex_cache::rewind_subpath()
|
||||||
|
{
|
||||||
|
current_segment_ = current_subpath_->vector.begin();
|
||||||
|
//All subpaths contain at least one segment (i.e. the starting point)
|
||||||
|
segment_starting_point_ = current_position_ = current_segment_->pos;
|
||||||
|
position_in_segment_ = 0;
|
||||||
|
angle_valid_ = false;
|
||||||
|
position_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vertex_cache::reset()
|
||||||
|
{
|
||||||
|
initialized_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool vertex_cache::next_segment()
|
||||||
|
{
|
||||||
|
segment_starting_point_ = current_segment_->pos; //Next segments starts at the end of the current one
|
||||||
|
if (current_segment_ == current_subpath_->vector.end()) return false;
|
||||||
|
current_segment_++;
|
||||||
|
angle_valid_ = false;
|
||||||
|
if (current_segment_ == current_subpath_->vector.end()) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool vertex_cache::previous_segment()
|
||||||
|
{
|
||||||
|
if (current_segment_ == current_subpath_->vector.begin()) return false;
|
||||||
|
current_segment_--;
|
||||||
|
angle_valid_ = false;
|
||||||
|
if (current_segment_ == current_subpath_->vector.begin())
|
||||||
|
{
|
||||||
|
//First segment is special
|
||||||
|
segment_starting_point_ = current_segment_->pos;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
segment_starting_point_ = (current_segment_-1)->pos;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex_cache & vertex_cache::get_offseted(double offset, double region_width)
|
||||||
|
{
|
||||||
|
if (fabs(offset) < 0.01)
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex_cache_ptr offseted_line;
|
||||||
|
offseted_lines_map::iterator pos = offseted_lines_.find(offset);
|
||||||
|
if (pos != offseted_lines_.end())
|
||||||
|
{
|
||||||
|
offseted_line = pos->second;
|
||||||
|
} else {
|
||||||
|
offset_converter<vertex_cache> converter(*this);
|
||||||
|
converter.set_offset(offset);
|
||||||
|
offseted_line = vertex_cache_ptr(new vertex_cache(converter));
|
||||||
|
}
|
||||||
|
offseted_line->reset();
|
||||||
|
offseted_line->next_subpath(); //TODO: Multiple subpath support
|
||||||
|
double seek = (position_ + region_width/2.) * offseted_line->length() / length() - region_width/2.;
|
||||||
|
if (seek < 0) seek = 0;
|
||||||
|
if (seek > offseted_line->length()) seek = offseted_line->length();
|
||||||
|
offseted_line->move(seek);
|
||||||
|
offseted_lines_[offset] = offseted_line;
|
||||||
|
return *offseted_line;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool vertex_cache::forward(double length)
|
||||||
|
{
|
||||||
|
if (length < 0)
|
||||||
|
{
|
||||||
|
MAPNIK_LOG_ERROR(vertex_cache) << "vertex_cache::forward() called with negative argument!\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return move(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool vertex_cache::backward(double length)
|
||||||
|
{
|
||||||
|
if (length < 0)
|
||||||
|
{
|
||||||
|
MAPNIK_LOG_ERROR(vertex_cache) << "vertex_cache::backward() called with negative argument!\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return move(-length);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool vertex_cache::move(double length)
|
||||||
|
{
|
||||||
|
position_ += length;
|
||||||
|
length += position_in_segment_;
|
||||||
|
while (length >= current_segment_->length)
|
||||||
|
{
|
||||||
|
length -= current_segment_->length;
|
||||||
|
if (!next_segment()) return false; //Skip all complete segments
|
||||||
|
}
|
||||||
|
while (length < 0)
|
||||||
|
{
|
||||||
|
if (!previous_segment()) return false;
|
||||||
|
length += current_segment_->length;
|
||||||
|
}
|
||||||
|
double factor = length / current_segment_->length;
|
||||||
|
position_in_segment_ = length;
|
||||||
|
current_position_ = segment_starting_point_ + (current_segment_->pos - segment_starting_point_) * factor;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vertex_cache::rewind(unsigned)
|
||||||
|
{
|
||||||
|
vertex_subpath_ = subpaths_.begin();
|
||||||
|
vertex_segment_ = vertex_subpath_->vector.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned vertex_cache::vertex(double *x, double *y)
|
||||||
|
{
|
||||||
|
if (vertex_segment_ == vertex_subpath_->vector.end())
|
||||||
|
{
|
||||||
|
vertex_subpath_++;
|
||||||
|
if (vertex_subpath_ == subpaths_.end()) return agg::path_cmd_stop;
|
||||||
|
vertex_segment_ = vertex_subpath_->vector.begin();
|
||||||
|
}
|
||||||
|
*x = vertex_segment_->pos.x;
|
||||||
|
*y = vertex_segment_->pos.y;
|
||||||
|
unsigned cmd = (vertex_segment_ == vertex_subpath_->vector.begin()) ? agg::path_cmd_move_to : agg::path_cmd_line_to;
|
||||||
|
vertex_segment_++;
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
vertex_cache::state vertex_cache::save_state() const
|
||||||
|
{
|
||||||
|
state s;
|
||||||
|
s.current_segment = current_segment_;
|
||||||
|
s.position_in_segment = position_in_segment_;
|
||||||
|
s.current_position = current_position_;
|
||||||
|
s.segment_starting_point = segment_starting_point_;
|
||||||
|
s.position_ = position_;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vertex_cache::restore_state(state const& s)
|
||||||
|
{
|
||||||
|
current_segment_ = s.current_segment;
|
||||||
|
position_in_segment_ = s.position_in_segment;
|
||||||
|
current_position_ = s.current_position;
|
||||||
|
segment_starting_point_ = s.segment_starting_point;
|
||||||
|
position_ = s.position_;
|
||||||
|
angle_valid_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} //ns mapnik
|
|
@ -38,62 +38,6 @@ static const char * halo_rasterizer_strings[] = {
|
||||||
IMPLEMENT_ENUM( halo_rasterizer_e, 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)
|
text_symbolizer::text_symbolizer(text_placements_ptr placements)
|
||||||
: symbolizer_base(),
|
: symbolizer_base(),
|
||||||
placement_options_(placements),
|
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,
|
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)
|
text_placements_ptr placements)
|
||||||
: symbolizer_base(),
|
: symbolizer_base(),
|
||||||
placement_options_(placements),
|
placement_options_(placements),
|
||||||
|
@ -115,7 +59,7 @@ text_symbolizer::text_symbolizer(expression_ptr name, std::string const& face_na
|
||||||
set_fill(fill);
|
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)
|
text_placements_ptr placements)
|
||||||
: symbolizer_base(),
|
: symbolizer_base(),
|
||||||
placement_options_(placements),
|
placement_options_(placements),
|
||||||
|
@ -167,22 +111,22 @@ void text_symbolizer::set_orientation(expression_ptr orientation)
|
||||||
|
|
||||||
std::string const& text_symbolizer::get_face_name() const
|
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)
|
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)
|
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
|
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
|
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
|
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)
|
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
|
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
|
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)
|
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)
|
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
|
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)
|
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
|
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)
|
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
|
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)
|
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
|
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;
|
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;
|
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;
|
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)
|
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
|
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)
|
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
|
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)
|
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
|
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)
|
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
|
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)
|
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)
|
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;
|
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;
|
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)
|
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
|
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)
|
void text_symbolizer::set_vertical_alignment(vertical_alignment_e valign)
|
||||||
|
|
|
@ -400,6 +400,7 @@ compile_get_opt_attr(label_placement_e);
|
||||||
compile_get_opt_attr(vertical_alignment_e);
|
compile_get_opt_attr(vertical_alignment_e);
|
||||||
compile_get_opt_attr(horizontal_alignment_e);
|
compile_get_opt_attr(horizontal_alignment_e);
|
||||||
compile_get_opt_attr(justify_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(halo_rasterizer_e);
|
||||||
compile_get_opt_attr(expression_ptr);
|
compile_get_opt_attr(expression_ptr);
|
||||||
compile_get_attr(std::string);
|
compile_get_attr(std::string);
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
<Datasource>
|
<Datasource>
|
||||||
<Parameter name="type">csv</Parameter>
|
<Parameter name="type">csv</Parameter>
|
||||||
<Parameter name="file">../data/points.csv</Parameter>
|
<Parameter name="file">../data/points.csv</Parameter>
|
||||||
|
<Parameter name="extent">-0.05, -0.01, 0.95, 0.01</Parameter>
|
||||||
</Datasource>
|
</Datasource>
|
||||||
</Layer>
|
</Layer>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue