2012-03-05 16:49:54 +01:00
/*****************************************************************************
*
* 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
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2012-03-06 15:18:11 +01:00
//mapnik
2014-06-12 16:14:05 +02:00
# include <mapnik/make_unique.hpp>
2012-03-05 16:49:54 +01:00
# include <mapnik/xml_tree.hpp>
2013-01-16 13:42:20 +01:00
# include <mapnik/xml_attribute_cast.hpp>
2012-03-06 15:18:11 +01:00
# include <mapnik/util/conversions.hpp>
# include <mapnik/enumeration.hpp>
2012-03-06 15:47:08 +01:00
# include <mapnik/color_factory.hpp>
2012-03-07 03:57:31 +01:00
# include <mapnik/gamma_method.hpp>
2013-01-04 03:05:40 +01:00
# include <mapnik/rule.hpp>
2012-03-07 19:16:41 +01:00
# include <mapnik/feature_type_style.hpp>
2013-11-08 05:09:22 +01:00
# include <mapnik/text/text_properties.hpp>
2012-03-12 01:09:26 +01:00
# include <mapnik/config_error.hpp>
2012-10-02 00:01:12 +02:00
# include <mapnik/raster_colorizer.hpp>
2014-04-30 09:11:27 +02:00
# include <mapnik/expression.hpp>
2013-11-29 16:46:23 +01:00
// stl
# include <type_traits>
2012-03-06 15:18:11 +01:00
2012-03-05 16:49:54 +01:00
namespace mapnik
{
2012-03-06 15:18:11 +01:00
class boolean ;
template < typename T >
struct name_trait
{
static std : : string name ( )
{
return " <unknown> " ;
}
// missing name_trait for type ...
// if you get here you are probably using a new type
// in the XML file. Just add a name trait for the new
// type below.
2013-11-29 16:46:23 +01:00
static_assert ( sizeof ( T ) = = 0 , " missing name_trait for the type " ) ;
2012-03-06 15:18:11 +01:00
} ;
2012-03-13 15:56:11 +01:00
# define DEFINE_NAME_TRAIT( type, type_name ) \
2012-03-06 15:18:11 +01:00
template < > \
struct name_trait < type > \
{ \
static std : : string name ( ) { return std : : string ( " type " ) + type_name ; } \
} ;
DEFINE_NAME_TRAIT ( double , " double " )
DEFINE_NAME_TRAIT ( float , " float " )
DEFINE_NAME_TRAIT ( unsigned , " unsigned " )
DEFINE_NAME_TRAIT ( boolean , " boolean " )
2013-01-04 05:05:06 +01:00
# ifdef BIGINT
2012-12-03 14:12:09 +01:00
DEFINE_NAME_TRAIT ( mapnik : : value_integer , " long long " )
2013-01-04 05:05:06 +01:00
# else
DEFINE_NAME_TRAIT ( mapnik : : value_integer , " int " )
# endif
2012-03-06 15:18:11 +01:00
DEFINE_NAME_TRAIT ( std : : string , " string " )
DEFINE_NAME_TRAIT ( color , " color " )
2012-03-07 19:16:41 +01:00
DEFINE_NAME_TRAIT ( expression_ptr , " expression_ptr " )
2012-03-06 15:18:11 +01:00
template < typename ENUM , int MAX >
struct name_trait < mapnik : : enumeration < ENUM , MAX > >
{
2014-07-07 19:23:15 +02:00
using Enum = enumeration < ENUM , MAX > ;
2012-03-06 15:18:11 +01:00
static std : : string name ( )
{
std : : string value_list ( " one of [ " ) ;
for ( unsigned i = 0 ; i < Enum : : MAX ; + + i )
{
value_list + = Enum : : get_string ( i ) ;
if ( i + 1 < Enum : : MAX ) value_list + = " , " ;
}
value_list + = " ] " ;
return value_list ;
}
} ;
2012-03-11 23:24:28 +01:00
xml_tree : : xml_tree ( std : : string const & encoding )
: node_ ( * this , " <root> " ) ,
2014-04-30 19:42:05 +02:00
file_ ( )
2012-03-05 16:49:54 +01:00
{
2012-03-12 02:12:58 +01:00
node_ . set_processed ( true ) ; //root node is always processed
2012-03-05 16:49:54 +01:00
}
2014-03-10 21:00:14 +01:00
void xml_tree : : set_filename ( std : : string const & fn )
2012-03-05 16:49:54 +01:00
{
file_ = fn ;
}
2012-03-12 01:09:26 +01:00
std : : string const & xml_tree : : filename ( ) const
2012-03-05 16:49:54 +01:00
{
return file_ ;
}
2012-03-06 15:18:11 +01:00
xml_node & xml_tree : : root ( )
2012-03-05 16:49:54 +01:00
{
return node_ ;
}
2012-07-04 20:51:34 +02:00
const xml_node & xml_tree : : root ( ) const
{
return node_ ;
}
2014-04-29 02:10:00 +02:00
xml_attribute : : xml_attribute ( const char * value_ )
: value ( value_ ) ,
processed ( false )
2012-03-07 03:57:31 +01:00
{
}
2012-04-06 21:58:08 +02:00
node_not_found : : node_not_found ( std : : string const & node_name )
2013-01-16 13:42:20 +01:00
: node_name_ ( node_name ) { }
2012-03-07 03:57:31 +01:00
const char * node_not_found : : what ( ) const throw ( )
{
return ( " Node " + node_name_ + " not found " ) . c_str ( ) ;
}
2013-01-16 13:42:20 +01:00
node_not_found : : ~ node_not_found ( ) throw ( ) { }
2012-03-07 03:57:31 +01:00
attribute_not_found : : attribute_not_found (
std : : string const & node_name ,
std : : string const & attribute_name )
:
2012-03-13 15:56:11 +01:00
node_name_ ( node_name ) ,
2013-01-16 13:42:20 +01:00
attribute_name_ ( attribute_name ) { }
2012-03-07 03:57:31 +01:00
const char * attribute_not_found : : what ( ) const throw ( )
{
return ( " Attribute ' " + attribute_name_ + " ' not found in node ' " + node_name_ + " ' " ) . c_str ( ) ;
}
2013-01-16 13:42:20 +01:00
attribute_not_found : : ~ attribute_not_found ( ) throw ( ) { }
2012-03-07 03:57:31 +01:00
more_than_one_child : : more_than_one_child ( std : : string const & node_name )
2013-01-16 13:42:20 +01:00
: node_name_ ( node_name ) { }
2012-03-07 03:57:31 +01:00
const char * more_than_one_child : : what ( ) const throw ( )
{
return ( " More than one child node in node ' " + node_name_ + " ' " ) . c_str ( ) ;
}
2013-01-16 13:42:20 +01:00
more_than_one_child : : ~ more_than_one_child ( ) throw ( ) { }
2012-03-05 16:49:54 +01:00
2014-04-29 02:10:00 +02:00
xml_node : : xml_node ( xml_tree & tree , std : : string & & name , unsigned line , bool is_text )
2012-03-05 16:49:54 +01:00
: tree_ ( tree ) ,
2014-04-29 02:10:00 +02:00
name_ ( std : : move ( name ) ) ,
2012-07-03 13:45:07 +02:00
is_text_ ( is_text ) ,
2012-03-05 16:49:54 +01:00
line_ ( line ) ,
2013-01-16 13:42:20 +01:00
processed_ ( false ) { }
2012-03-05 16:49:54 +01:00
2012-03-12 01:09:26 +01:00
std : : string xml_node : : xml_text = " <xmltext> " ;
std : : string const & xml_node : : name ( ) const
2012-03-05 16:49:54 +01:00
{
2012-07-03 13:45:07 +02:00
if ( ! is_text_ )
2013-02-23 03:58:36 +01:00
{
2012-03-05 16:49:54 +01:00
return name_ ;
2013-02-23 03:58:36 +01:00
}
2012-03-05 16:49:54 +01:00
else
2013-02-23 03:58:36 +01:00
{
2012-03-12 01:09:26 +01:00
return xml_text ;
2013-02-23 03:58:36 +01:00
}
2012-03-05 16:49:54 +01:00
}
2012-03-12 01:09:26 +01:00
std : : string const & xml_node : : text ( ) const
2012-03-05 16:49:54 +01:00
{
2012-07-03 13:45:07 +02:00
if ( is_text_ )
2012-03-12 02:12:58 +01:00
{
processed_ = true ;
2012-03-05 16:49:54 +01:00
return name_ ;
2013-02-23 03:58:36 +01:00
}
else
2012-03-12 02:12:58 +01:00
{
2012-03-12 01:09:26 +01:00
throw config_error ( " text() called on non - text node " , *this) ;
2012-03-12 02:12:58 +01:00
}
2012-03-12 01:09:26 +01:00
}
std : : string const & xml_node : : filename ( ) const
{
return tree_ . filename ( ) ;
2012-03-05 16:49:54 +01:00
}
2012-03-07 03:57:31 +01:00
bool xml_node : : is_text ( ) const
2012-03-05 16:49:54 +01:00
{
2012-07-03 13:45:07 +02:00
return is_text_ ;
2012-03-07 03:57:31 +01:00
}
bool xml_node : : is ( std : : string const & name ) const
{
if ( name_ = = name )
{
processed_ = true ;
return true ;
}
return false ;
2012-03-05 16:49:54 +01:00
}
2014-04-29 02:10:00 +02:00
xml_node & xml_node : : add_child ( std : : string & & name , unsigned line , bool is_text )
2012-03-05 16:49:54 +01:00
{
2014-04-29 02:10:00 +02:00
children_ . emplace_back ( tree_ , std : : move ( name ) , line , is_text ) ;
2012-03-05 16:49:54 +01:00
return children_ . back ( ) ;
}
2014-04-29 02:10:00 +02:00
void xml_node : : add_attribute ( const char * name , const char * value )
2012-03-07 03:57:31 +01:00
{
2014-05-06 01:02:42 +02:00
attributes_ . insert ( std : : make_pair ( name , xml_attribute ( value ) ) ) ;
2012-03-07 03:57:31 +01:00
}
2012-03-08 01:29:19 +01:00
xml_node : : attribute_map const & xml_node : : get_attributes ( ) const
{
return attributes_ ;
}
2012-03-07 03:57:31 +01:00
void xml_node : : set_processed ( bool processed ) const
{
processed_ = processed ;
}
2012-03-08 18:51:23 +01:00
bool xml_node : : processed ( ) const
{
return processed_ ;
}
2014-04-29 02:10:00 +02:00
std : : size_t xml_node : : size ( ) const
{
return children_ . size ( ) ;
}
2012-03-07 03:57:31 +01:00
xml_node : : const_iterator xml_node : : begin ( ) const
{
return children_ . begin ( ) ;
}
xml_node : : const_iterator xml_node : : end ( ) const
{
return children_ . end ( ) ;
}
2012-03-07 02:23:16 +01:00
xml_node & xml_node : : get_child ( std : : string const & name )
2012-03-06 15:18:11 +01:00
{
std : : list < xml_node > : : iterator itr = children_ . begin ( ) ;
std : : list < xml_node > : : iterator end = children_ . end ( ) ;
2014-07-08 17:38:15 +02:00
for ( ; itr ! = end ; + + itr )
2012-03-06 15:18:11 +01:00
{
2012-07-03 13:45:07 +02:00
if ( ! ( itr - > is_text_ ) & & itr - > name_ = = name )
2012-03-06 15:18:11 +01:00
{
itr - > set_processed ( true ) ;
return * itr ;
}
}
throw node_not_found ( name ) ;
}
2012-03-07 03:57:31 +01:00
xml_node const & xml_node : : get_child ( std : : string const & name ) const
{
xml_node const * node = get_opt_child ( name ) ;
if ( ! node ) throw node_not_found ( name ) ;
return * node ;
}
xml_node const * xml_node : : get_opt_child ( std : : string const & name ) const
{
const_iterator itr = children_ . begin ( ) ;
const_iterator end = children_ . end ( ) ;
for ( ; itr ! = end ; itr + + )
{
2012-07-03 13:45:07 +02:00
if ( ! ( itr - > is_text_ ) & & itr - > name_ = = name )
2012-03-07 03:57:31 +01:00
{
itr - > set_processed ( true ) ;
return & ( * itr ) ;
}
}
return 0 ;
}
bool xml_node : : has_child ( std : : string const & name ) const
{
return get_opt_child ( name ) ! = 0 ;
}
2012-03-06 15:18:11 +01:00
template < typename T >
boost : : optional < T > xml_node : : get_opt_attr ( std : : string const & name ) const
{
2014-04-29 02:10:00 +02:00
if ( attributes_ . empty ( ) ) return boost : : optional < T > ( ) ;
2012-03-06 15:18:11 +01:00
std : : map < std : : string , xml_attribute > : : const_iterator itr = attributes_ . find ( name ) ;
if ( itr = = attributes_ . end ( ) ) return boost : : optional < T > ( ) ;
2012-03-07 03:57:31 +01:00
itr - > second . processed = true ;
2013-01-16 13:42:20 +01:00
boost : : optional < T > result = xml_attribute_cast < T > ( tree_ , std : : string ( itr - > second . value ) ) ;
2012-03-06 15:18:11 +01:00
if ( ! result )
{
throw config_error ( std : : string ( " Failed to parse attribute ' " ) +
name + " '. Expected " + name_trait < T > : : name ( ) +
2012-03-12 02:12:58 +01:00
" but got ' " + itr - > second . value + " ' " , * this ) ;
2012-03-07 03:57:31 +01:00
}
return result ;
}
template < typename T >
2013-01-04 03:05:40 +01:00
T xml_node : : get_attr ( std : : string const & name , T const & default_opt_value ) const
2012-03-07 03:57:31 +01:00
{
boost : : optional < T > value = get_opt_attr < T > ( name ) ;
if ( value ) return * value ;
2013-01-04 03:05:40 +01:00
return default_opt_value ;
2012-03-07 03:57:31 +01:00
}
template < typename T >
T xml_node : : get_attr ( std : : string const & name ) const
{
boost : : optional < T > value = get_opt_attr < T > ( name ) ;
if ( value ) return * value ;
throw attribute_not_found ( name_ , name ) ;
}
2014-05-27 22:35:52 +02:00
std : : string const & xml_node : : get_text ( ) const
2012-03-07 03:57:31 +01:00
{
2014-05-28 12:04:47 +02:00
// FIXME : return boost::optional<std::string const&>
2014-04-29 02:10:00 +02:00
if ( children_ . empty ( ) )
2012-03-07 03:57:31 +01:00
{
2012-07-04 20:51:34 +02:00
if ( is_text_ )
{
return name_ ;
2013-02-23 03:58:36 +01:00
}
else
2012-07-04 20:51:34 +02:00
{
2014-05-28 12:04:47 +02:00
const static std : : string empty ;
return empty ;
2012-07-04 20:51:34 +02:00
}
2012-03-07 03:57:31 +01:00
}
if ( children_ . size ( ) = = 1 )
{
return children_ . front ( ) . text ( ) ;
}
throw more_than_one_child ( name_ ) ;
}
template < typename T >
T xml_node : : get_value ( ) const
{
2013-01-16 13:42:20 +01:00
boost : : optional < T > result = xml_attribute_cast < T > ( tree_ , get_text ( ) ) ;
2012-03-07 03:57:31 +01:00
if ( ! result )
{
2012-03-12 01:09:26 +01:00
throw config_error ( std : : string ( " Failed to parse value. Expected " )
+ name_trait < T > : : name ( ) +
" but got ' " + get_text ( ) + " ' " , * this ) ;
2012-03-06 15:18:11 +01:00
}
2012-03-07 19:16:41 +01:00
return * result ;
2012-03-06 15:18:11 +01:00
}
2012-03-08 18:51:23 +01:00
unsigned xml_node : : line ( ) const
{
return line_ ;
}
2012-12-07 05:15:27 +01:00
std : : string xml_node : : line_to_string ( ) const
{
std : : string number ;
util : : to_string ( number , line_ ) ;
return number ;
}
2012-03-07 03:57:31 +01:00
# define compile_get_opt_attr(T) template boost::optional<T> xml_node::get_opt_attr<T>(std::string const&) const
2012-03-07 19:16:41 +01:00
# define compile_get_attr(T) template T xml_node::get_attr<T>(std::string const&) const; template T xml_node::get_attr<T>(std::string const&, T const&) const
# define compile_get_value(T) template T xml_node::get_value<T>() const
2012-03-05 16:49:54 +01:00
2012-03-07 19:16:41 +01:00
compile_get_opt_attr ( boolean ) ;
2012-03-07 03:57:31 +01:00
compile_get_opt_attr ( std : : string ) ;
compile_get_opt_attr ( unsigned ) ;
2012-12-03 14:12:09 +01:00
compile_get_opt_attr ( mapnik : : value_integer ) ;
2012-03-07 03:57:31 +01:00
compile_get_opt_attr ( float ) ;
compile_get_opt_attr ( double ) ;
compile_get_opt_attr ( color ) ;
compile_get_opt_attr ( gamma_method_e ) ;
2012-03-07 19:16:41 +01:00
compile_get_opt_attr ( line_join_e ) ;
compile_get_opt_attr ( line_cap_e ) ;
compile_get_opt_attr ( text_transform_e ) ;
compile_get_opt_attr ( label_placement_e ) ;
compile_get_opt_attr ( vertical_alignment_e ) ;
compile_get_opt_attr ( horizontal_alignment_e ) ;
compile_get_opt_attr ( justify_alignment_e ) ;
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
2013-11-22 09:06:32 +01:00
compile_get_opt_attr ( text_upright_e ) ;
2013-03-09 05:32:39 +01:00
compile_get_opt_attr ( halo_rasterizer_e ) ;
2012-03-11 23:24:28 +01:00
compile_get_opt_attr ( expression_ptr ) ;
2012-03-07 19:16:41 +01:00
compile_get_attr ( std : : string ) ;
compile_get_attr ( filter_mode_e ) ;
compile_get_attr ( point_placement_e ) ;
2013-05-22 04:21:35 +02:00
compile_get_attr ( debug_symbolizer_mode_e ) ;
2012-03-07 19:16:41 +01:00
compile_get_attr ( marker_placement_e ) ;
2012-11-21 02:58:39 +01:00
compile_get_attr ( marker_multi_policy_e ) ;
2012-03-07 19:16:41 +01:00
compile_get_attr ( pattern_alignment_e ) ;
2012-05-12 02:40:21 +02:00
compile_get_attr ( line_rasterizer_e ) ;
2012-03-07 19:16:41 +01:00
compile_get_attr ( colorizer_mode ) ;
compile_get_attr ( double ) ;
2012-12-03 14:12:09 +01:00
compile_get_value ( value_integer ) ;
2012-03-07 19:16:41 +01:00
compile_get_value ( double ) ;
compile_get_value ( expression_ptr ) ;
2012-03-05 16:49:54 +01:00
} //ns mapnik