2010-05-28 16:34:34 +00:00
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* This file is part of Mapnik (c++ mapping toolkit)
|
|
|
|
*
|
2011-10-23 13:04:25 +00:00
|
|
|
* Copyright (C) 2011 Artem Pavlenko
|
2010-05-28 16:34:34 +00:00
|
|
|
*
|
|
|
|
* 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/color_factory.hpp>
|
2010-06-08 10:16:22 +00:00
|
|
|
|
2010-05-28 16:34:58 +00:00
|
|
|
#include <mapnik/svg/svg_parser.hpp>
|
|
|
|
#include <mapnik/svg/svg_path_parser.hpp>
|
2010-05-28 16:34:34 +00:00
|
|
|
|
2010-06-13 13:45:59 +00:00
|
|
|
#include "agg_ellipse.h"
|
|
|
|
#include "agg_rounded_rect.h"
|
2011-01-26 01:18:40 +00:00
|
|
|
#include "agg_span_gradient.h"
|
2010-06-13 13:45:59 +00:00
|
|
|
|
2010-05-28 16:34:34 +00:00
|
|
|
#include <boost/spirit/include/qi.hpp>
|
2011-01-26 01:18:40 +00:00
|
|
|
#include <boost/spirit/include/phoenix_core.hpp>
|
|
|
|
#include <boost/spirit/include/phoenix_operator.hpp>
|
2010-05-28 16:34:34 +00:00
|
|
|
#include <boost/fusion/include/std_pair.hpp>
|
|
|
|
#include <boost/foreach.hpp>
|
2011-01-26 01:18:40 +00:00
|
|
|
#include <boost/algorithm/string/predicate.hpp>
|
|
|
|
#include <boost/lexical_cast.hpp>
|
2010-05-28 16:34:34 +00:00
|
|
|
|
|
|
|
#include <iostream>
|
|
|
|
#include <string>
|
|
|
|
#include <stdexcept>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
namespace mapnik { namespace svg {
|
|
|
|
|
|
|
|
|
2012-02-02 01:53:35 +00:00
|
|
|
typedef std::vector<std::pair<double, agg::rgba8> > color_lookup_type;
|
2011-01-26 01:18:40 +00:00
|
|
|
|
2012-02-02 01:53:35 +00:00
|
|
|
namespace qi = boost::spirit::qi;
|
2010-05-28 16:34:34 +00:00
|
|
|
|
2012-02-02 01:53:35 +00:00
|
|
|
typedef std::vector<std::pair<std::string, std::string> > pairs_type;
|
2010-05-28 16:34:34 +00:00
|
|
|
|
2012-02-02 01:53:35 +00:00
|
|
|
template <typename Iterator,typename SkipType>
|
|
|
|
struct key_value_sequence_ordered
|
|
|
|
: qi::grammar<Iterator, pairs_type(), SkipType>
|
2010-06-02 11:03:30 +00:00
|
|
|
{
|
2012-02-02 01:53:35 +00:00
|
|
|
key_value_sequence_ordered()
|
|
|
|
: key_value_sequence_ordered::base_type(query)
|
|
|
|
{
|
|
|
|
query = pair >> *( qi::lit(';') >> pair);
|
|
|
|
pair = key >> -(':' >> value);
|
|
|
|
key = qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9-");
|
|
|
|
value = +(qi::char_ - qi::lit(';'));
|
|
|
|
}
|
|
|
|
|
|
|
|
qi::rule<Iterator, pairs_type(), SkipType> query;
|
|
|
|
qi::rule<Iterator, std::pair<std::string, std::string>(), SkipType> pair;
|
|
|
|
qi::rule<Iterator, std::string(), SkipType> key, value;
|
|
|
|
};
|
2010-05-28 16:34:34 +00:00
|
|
|
|
|
|
|
agg::rgba8 parse_color(const char* str)
|
|
|
|
{
|
|
|
|
mapnik::color c(100,100,100);
|
|
|
|
try
|
|
|
|
{
|
2010-06-02 11:03:30 +00:00
|
|
|
mapnik::color_factory::init_from_string(c,str);
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
2012-02-02 01:53:35 +00:00
|
|
|
catch (mapnik::config_error & ex)
|
2010-05-28 16:34:34 +00:00
|
|
|
{
|
2010-06-02 11:03:30 +00:00
|
|
|
std::cerr << ex.what() << std::endl;
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
|
|
|
return agg::rgba8(c.red(), c.green(), c.blue(), c.alpha());
|
|
|
|
}
|
|
|
|
|
|
|
|
double parse_double(const char* str)
|
|
|
|
{
|
|
|
|
using namespace boost::spirit::qi;
|
|
|
|
double val = 0.0;
|
|
|
|
parse(str, str+ strlen(str),double_,val);
|
2012-02-02 01:53:35 +00:00
|
|
|
return val;
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
|
|
|
|
2011-01-26 01:18:40 +00:00
|
|
|
/*
|
|
|
|
* parse a double that might end with a %
|
|
|
|
* if it does then set the ref bool true and divide the result by 100
|
|
|
|
*/
|
|
|
|
double parse_double_optional_percent(const char* str, bool &percent)
|
|
|
|
{
|
|
|
|
using namespace boost::spirit::qi;
|
|
|
|
using boost::phoenix::ref;
|
2011-07-09 00:28:41 +00:00
|
|
|
using qi::_1;
|
2011-01-26 01:18:40 +00:00
|
|
|
|
|
|
|
double val = 0.0;
|
|
|
|
char unit='\0';
|
|
|
|
parse(str, str+ strlen(str),double_[ref(val)=_1] >> *char_('%')[ref(unit)=_1]);
|
|
|
|
if (unit =='%')
|
|
|
|
{
|
|
|
|
percent = true;
|
|
|
|
val/=100.0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
percent = false;
|
|
|
|
}
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2010-05-28 16:34:34 +00:00
|
|
|
bool parse_style (const char* str, pairs_type & v)
|
|
|
|
{
|
|
|
|
using namespace boost::spirit::qi;
|
|
|
|
typedef boost::spirit::ascii::space_type skip_type;
|
|
|
|
key_value_sequence_ordered<const char*, skip_type> kv_parser;
|
|
|
|
return phrase_parse(str, str + strlen(str), kv_parser, skip_type(), v);
|
|
|
|
}
|
|
|
|
|
2012-02-02 01:53:35 +00:00
|
|
|
svg_parser::svg_parser(svg_converter<svg_path_adapter,
|
|
|
|
agg::pod_bvector<mapnik::svg::path_attributes> > & path)
|
2010-06-11 14:09:05 +00:00
|
|
|
: path_(path),
|
|
|
|
is_defs_(false) {}
|
2012-02-02 01:53:35 +00:00
|
|
|
|
2010-05-28 16:34:34 +00:00
|
|
|
svg_parser::~svg_parser() {}
|
|
|
|
|
|
|
|
void svg_parser::parse(std::string const& filename)
|
|
|
|
{
|
|
|
|
xmlTextReaderPtr reader = xmlNewTextReaderFilename(filename.c_str());
|
2012-02-02 01:53:35 +00:00
|
|
|
if (reader != 0)
|
2010-05-28 16:34:34 +00:00
|
|
|
{
|
2010-06-02 11:03:30 +00:00
|
|
|
int ret = xmlTextReaderRead(reader);
|
2012-02-02 01:53:35 +00:00
|
|
|
while (ret == 1)
|
2010-06-02 11:03:30 +00:00
|
|
|
{
|
|
|
|
process_node(reader);
|
|
|
|
ret = xmlTextReaderRead(reader);
|
|
|
|
}
|
|
|
|
xmlFreeTextReader(reader);
|
2012-02-02 01:53:35 +00:00
|
|
|
if (ret != 0)
|
2010-06-02 11:03:30 +00:00
|
|
|
{
|
|
|
|
std::cerr << "Failed to parse " << filename << std::endl;
|
|
|
|
}
|
2010-05-28 16:34:34 +00:00
|
|
|
} else {
|
2010-06-02 11:03:30 +00:00
|
|
|
std::cerr << "Unable to open " << filename << std::endl;
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void svg_parser::process_node(xmlTextReaderPtr reader)
|
|
|
|
{
|
|
|
|
int node_type = xmlTextReaderNodeType(reader);
|
|
|
|
switch (node_type)
|
|
|
|
{
|
|
|
|
case 1: //start element
|
2010-06-02 11:03:30 +00:00
|
|
|
start_element(reader);
|
|
|
|
break;
|
2010-05-28 16:34:34 +00:00
|
|
|
case 15:// end element
|
2010-06-02 11:03:30 +00:00
|
|
|
end_element(reader);
|
|
|
|
break;
|
2010-05-28 16:34:34 +00:00
|
|
|
default:
|
2010-06-02 11:03:30 +00:00
|
|
|
break;
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void svg_parser::start_element(xmlTextReaderPtr reader)
|
|
|
|
{
|
|
|
|
const xmlChar *name;
|
2012-02-02 01:53:35 +00:00
|
|
|
name = xmlTextReaderConstName(reader);
|
|
|
|
|
2010-06-11 14:09:05 +00:00
|
|
|
if (!is_defs_ && xmlStrEqual(name, BAD_CAST "g"))
|
2010-05-28 16:34:34 +00:00
|
|
|
{
|
2010-06-02 11:03:30 +00:00
|
|
|
path_.push_attr();
|
|
|
|
parse_attr(reader);
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
2010-06-11 14:09:05 +00:00
|
|
|
else if (xmlStrEqual(name, BAD_CAST "defs"))
|
|
|
|
{
|
|
|
|
if (xmlTextReaderIsEmptyElement(reader) == 0)
|
|
|
|
is_defs_ = true;
|
|
|
|
}
|
|
|
|
else if ( !is_defs_ && xmlStrEqual(name, BAD_CAST "path"))
|
2010-05-28 16:34:34 +00:00
|
|
|
{
|
2010-06-02 11:03:30 +00:00
|
|
|
parse_path(reader);
|
2012-02-02 01:53:35 +00:00
|
|
|
}
|
2010-06-14 10:29:37 +00:00
|
|
|
else if (!is_defs_ && xmlStrEqual(name, BAD_CAST "polygon") )
|
2010-05-28 16:34:34 +00:00
|
|
|
{
|
2010-06-02 11:03:30 +00:00
|
|
|
parse_polygon(reader);
|
2012-02-02 01:53:35 +00:00
|
|
|
}
|
2010-06-14 10:29:37 +00:00
|
|
|
else if (!is_defs_ && xmlStrEqual(name, BAD_CAST "polyline"))
|
2010-05-28 16:34:34 +00:00
|
|
|
{
|
2010-06-02 11:03:30 +00:00
|
|
|
parse_polyline(reader);
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
2010-06-11 14:09:05 +00:00
|
|
|
else if (!is_defs_ && xmlStrEqual(name, BAD_CAST "line"))
|
2010-05-28 16:34:34 +00:00
|
|
|
{
|
2010-06-02 11:03:30 +00:00
|
|
|
parse_line(reader);
|
2012-02-02 01:53:35 +00:00
|
|
|
}
|
2010-06-11 14:09:05 +00:00
|
|
|
else if (!is_defs_ && xmlStrEqual(name, BAD_CAST "rect"))
|
2010-05-28 16:34:34 +00:00
|
|
|
{
|
2010-06-02 11:03:30 +00:00
|
|
|
parse_rect(reader);
|
2012-02-02 01:53:35 +00:00
|
|
|
}
|
2010-06-11 14:09:05 +00:00
|
|
|
else if (!is_defs_ && xmlStrEqual(name, BAD_CAST "circle"))
|
2010-05-28 16:34:34 +00:00
|
|
|
{
|
2010-06-02 11:03:30 +00:00
|
|
|
parse_circle(reader);
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
2010-06-11 14:09:05 +00:00
|
|
|
else if (!is_defs_ && xmlStrEqual(name, BAD_CAST "ellipse"))
|
2010-05-28 16:34:34 +00:00
|
|
|
{
|
2010-06-02 11:03:30 +00:00
|
|
|
parse_ellipse(reader);
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
2011-01-26 01:18:40 +00:00
|
|
|
// the gradient tags *should* be in defs, but illustrator seems not to put them in there so
|
|
|
|
// accept them anywhere
|
|
|
|
else if (xmlStrEqual(name, BAD_CAST "linearGradient"))
|
|
|
|
{
|
|
|
|
parse_linear_gradient(reader);
|
|
|
|
}
|
|
|
|
else if (xmlStrEqual(name, BAD_CAST "radialGradient"))
|
|
|
|
{
|
|
|
|
parse_radial_gradient(reader);
|
|
|
|
}
|
|
|
|
else if (xmlStrEqual(name, BAD_CAST "stop"))
|
|
|
|
{
|
|
|
|
parse_gradient_stop(reader);
|
|
|
|
}
|
2011-01-26 09:17:01 +00:00
|
|
|
else if (!xmlStrEqual(name, BAD_CAST "svg"))
|
2011-01-26 01:18:40 +00:00
|
|
|
{
|
2011-01-26 09:17:01 +00:00
|
|
|
std::clog << "notice: unhandled svg element: " << name << "\n";
|
2011-01-26 01:18:40 +00:00
|
|
|
}
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void svg_parser::end_element(xmlTextReaderPtr reader)
|
|
|
|
{
|
|
|
|
const xmlChar *name;
|
2012-02-02 01:53:35 +00:00
|
|
|
name = xmlTextReaderConstName(reader);
|
2010-06-14 10:29:37 +00:00
|
|
|
if (!is_defs_ && xmlStrEqual(name, BAD_CAST "g"))
|
2010-05-28 16:34:34 +00:00
|
|
|
{
|
2010-06-02 11:03:30 +00:00
|
|
|
path_.pop_attr();
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
2010-06-11 14:09:05 +00:00
|
|
|
else if (xmlStrEqual(name, BAD_CAST "defs"))
|
|
|
|
{
|
|
|
|
is_defs_ = false;
|
|
|
|
}
|
2011-01-26 01:18:40 +00:00
|
|
|
else if ((xmlStrEqual(name, BAD_CAST "linearGradient")) || (xmlStrEqual(name, BAD_CAST "radialGradient")))
|
|
|
|
{
|
|
|
|
gradient_map_[temporary_gradient_.first] = temporary_gradient_.second;
|
|
|
|
}
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void svg_parser::parse_attr(const xmlChar * name, const xmlChar * value )
|
|
|
|
{
|
|
|
|
if (xmlStrEqual(name, BAD_CAST "transform"))
|
|
|
|
{
|
2010-06-02 11:03:30 +00:00
|
|
|
agg::trans_affine tr;
|
2010-06-14 13:18:17 +00:00
|
|
|
mapnik::svg::parse_transform((const char*) value,tr);
|
2010-06-02 11:03:30 +00:00
|
|
|
path_.transform().premultiply(tr);
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
|
|
|
else if (xmlStrEqual(name, BAD_CAST "fill"))
|
|
|
|
{
|
2010-06-02 11:03:30 +00:00
|
|
|
if (xmlStrEqual(value, BAD_CAST "none"))
|
|
|
|
{
|
|
|
|
path_.fill_none();
|
|
|
|
}
|
2011-01-26 01:18:40 +00:00
|
|
|
else if (boost::starts_with((const char*)value, "url(#"))
|
|
|
|
{
|
|
|
|
// see if we have a known gradient fill
|
|
|
|
std::string id = std::string((const char*)&value[5]);
|
|
|
|
// get rid of the trailing )
|
|
|
|
id.erase(id.end()-1);
|
|
|
|
if (gradient_map_.count(id) > 0)
|
|
|
|
{
|
|
|
|
path_.add_fill_gradient(gradient_map_[id]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::cerr << "Failed to find gradient fill: " << id << std::endl;
|
|
|
|
}
|
|
|
|
}
|
2010-06-02 11:03:30 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
path_.fill(parse_color((const char*) value));
|
|
|
|
}
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
|
|
|
else if (xmlStrEqual(name, BAD_CAST "fill-opacity"))
|
|
|
|
{
|
2012-02-02 01:53:35 +00:00
|
|
|
path_.fill_opacity(parse_double((const char*) value));
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
|
|
|
else if (xmlStrEqual(name, BAD_CAST "fill-rule"))
|
|
|
|
{
|
2010-06-02 11:03:30 +00:00
|
|
|
if (xmlStrEqual(value, BAD_CAST "evenodd"))
|
|
|
|
{
|
|
|
|
path_.even_odd(true);
|
|
|
|
}
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
|
|
|
else if (xmlStrEqual(name, BAD_CAST "stroke"))
|
|
|
|
{
|
2010-06-02 11:03:30 +00:00
|
|
|
if (xmlStrEqual(value, BAD_CAST "none"))
|
|
|
|
{
|
|
|
|
path_.stroke_none();
|
|
|
|
}
|
2011-01-26 01:18:40 +00:00
|
|
|
else if (boost::starts_with((const char*)value, "url(#"))
|
|
|
|
{
|
|
|
|
// see if we have a known gradient fill
|
|
|
|
std::string id = std::string((const char*)&value[5]);
|
|
|
|
// get rid of the trailing )
|
|
|
|
id.erase(id.end()-1);
|
|
|
|
if (gradient_map_.count(id) > 0)
|
|
|
|
{
|
|
|
|
path_.add_stroke_gradient(gradient_map_[id]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::cerr << "Failed to find gradient fill: " << id << std::endl;
|
|
|
|
}
|
|
|
|
}
|
2010-06-02 11:03:30 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
path_.stroke(parse_color((const char*) value));
|
|
|
|
}
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
|
|
|
else if (xmlStrEqual(name, BAD_CAST "stroke-width"))
|
|
|
|
{
|
2010-06-02 11:03:30 +00:00
|
|
|
path_.stroke_width(parse_double((const char*)value));
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
|
|
|
else if (xmlStrEqual(name, BAD_CAST "stroke-opacity"))
|
|
|
|
{
|
2010-06-02 11:03:30 +00:00
|
|
|
path_.stroke_opacity(parse_double((const char*)value));
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
|
|
|
else if(xmlStrEqual(name,BAD_CAST "stroke-width"))
|
|
|
|
{
|
2010-06-02 11:03:30 +00:00
|
|
|
path_.stroke_width(parse_double((const char*) value));
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
|
|
|
else if(xmlStrEqual(name,BAD_CAST "stroke-linecap"))
|
|
|
|
{
|
2012-02-02 01:53:35 +00:00
|
|
|
if(xmlStrEqual(value,BAD_CAST "butt"))
|
2010-06-02 11:03:30 +00:00
|
|
|
path_.line_cap(agg::butt_cap);
|
2012-02-02 01:53:35 +00:00
|
|
|
else if(xmlStrEqual(value,BAD_CAST "round"))
|
2010-06-02 11:03:30 +00:00
|
|
|
path_.line_cap(agg::round_cap);
|
2012-02-02 01:53:35 +00:00
|
|
|
else if(xmlStrEqual(value,BAD_CAST "square"))
|
2010-06-02 11:03:30 +00:00
|
|
|
path_.line_cap(agg::square_cap);
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
|
|
|
else if(xmlStrEqual(name,BAD_CAST "stroke-linejoin"))
|
|
|
|
{
|
2012-02-02 01:53:35 +00:00
|
|
|
if(xmlStrEqual(value,BAD_CAST "miter"))
|
2010-06-02 11:03:30 +00:00
|
|
|
path_.line_join(agg::miter_join);
|
2012-02-02 01:53:35 +00:00
|
|
|
else if(xmlStrEqual(value,BAD_CAST "round"))
|
2010-06-02 11:03:30 +00:00
|
|
|
path_.line_join(agg::round_join);
|
2012-02-02 01:53:35 +00:00
|
|
|
else if(xmlStrEqual(value,BAD_CAST "bevel"))
|
2010-06-02 11:03:30 +00:00
|
|
|
path_.line_join(agg::bevel_join);
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
|
|
|
else if(xmlStrEqual(name,BAD_CAST "stroke-miterlimit"))
|
|
|
|
{
|
2010-06-02 11:03:30 +00:00
|
|
|
path_.miter_limit(parse_double((const char*)value));
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
2012-02-02 01:53:35 +00:00
|
|
|
|
2010-05-28 16:34:34 +00:00
|
|
|
else if(xmlStrEqual(name, BAD_CAST "opacity"))
|
|
|
|
{
|
2010-06-02 11:03:30 +00:00
|
|
|
double opacity = parse_double((const char*)value);
|
2010-06-10 14:12:12 +00:00
|
|
|
path_.opacity(opacity);
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
2011-01-26 01:18:40 +00:00
|
|
|
else if (xmlStrEqual(name, BAD_CAST "visibility"))
|
|
|
|
{
|
|
|
|
path_.visibility(!xmlStrEqual(value, BAD_CAST "hidden"));
|
|
|
|
}
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void svg_parser::parse_attr(xmlTextReaderPtr reader)
|
|
|
|
{
|
|
|
|
const xmlChar *name, *value;
|
|
|
|
while (xmlTextReaderMoveToNextAttribute(reader))
|
|
|
|
{
|
2010-06-02 11:03:30 +00:00
|
|
|
name = xmlTextReaderConstName(reader);
|
|
|
|
value = xmlTextReaderConstValue(reader);
|
|
|
|
if (xmlStrEqual(name, BAD_CAST "style"))
|
|
|
|
{
|
2012-02-02 01:53:35 +00:00
|
|
|
typedef std::vector<std::pair<std::string,std::string> > cont_type;
|
2010-06-02 11:03:30 +00:00
|
|
|
typedef cont_type::value_type value_type;
|
|
|
|
cont_type vec;
|
|
|
|
parse_style((const char*)value, vec);
|
|
|
|
BOOST_FOREACH(value_type kv , vec )
|
|
|
|
{
|
2012-02-02 01:53:35 +00:00
|
|
|
parse_attr(BAD_CAST kv.first.c_str(),BAD_CAST kv.second.c_str());
|
|
|
|
}
|
2010-06-02 11:03:30 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
parse_attr(name,value);
|
|
|
|
}
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
void svg_parser::parse_path(xmlTextReaderPtr reader)
|
|
|
|
{
|
|
|
|
const xmlChar *value;
|
|
|
|
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "d");
|
2012-02-02 01:53:35 +00:00
|
|
|
if (value)
|
2010-05-28 16:34:34 +00:00
|
|
|
{
|
2010-06-02 11:03:30 +00:00
|
|
|
path_.begin_path();
|
|
|
|
parse_attr(reader);
|
2012-02-02 01:53:35 +00:00
|
|
|
|
2010-06-14 13:18:17 +00:00
|
|
|
if (!mapnik::svg::parse_path((const char*) value, path_))
|
2010-06-02 11:03:30 +00:00
|
|
|
{
|
|
|
|
std::runtime_error("can't parse PATH\n");
|
|
|
|
}
|
|
|
|
path_.end_path();
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void svg_parser::parse_polygon(xmlTextReaderPtr reader)
|
|
|
|
{
|
|
|
|
const xmlChar *value;
|
2012-02-02 01:53:35 +00:00
|
|
|
|
2010-05-28 16:34:34 +00:00
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "points");
|
2012-02-02 01:53:35 +00:00
|
|
|
if (value)
|
2010-05-28 16:34:34 +00:00
|
|
|
{
|
2010-06-02 11:03:30 +00:00
|
|
|
path_.begin_path();
|
|
|
|
parse_attr(reader);
|
|
|
|
if (!mapnik::svg::parse_points((const char*) value, path_))
|
|
|
|
{
|
|
|
|
throw std::runtime_error("Failed to parse <polygon>\n");
|
|
|
|
}
|
|
|
|
path_.close_subpath();
|
|
|
|
path_.end_path();
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void svg_parser::parse_polyline(xmlTextReaderPtr reader)
|
|
|
|
{
|
|
|
|
const xmlChar *value;
|
2012-02-02 01:53:35 +00:00
|
|
|
|
2010-05-28 16:34:34 +00:00
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "points");
|
2012-02-02 01:53:35 +00:00
|
|
|
if (value)
|
2010-05-28 16:34:34 +00:00
|
|
|
{
|
2010-06-02 11:03:30 +00:00
|
|
|
path_.begin_path();
|
|
|
|
parse_attr(reader);
|
|
|
|
if (!mapnik::svg::parse_points((const char*) value, path_))
|
|
|
|
{
|
|
|
|
throw std::runtime_error("Failed to parse <polygon>\n");
|
|
|
|
}
|
2012-02-02 01:53:35 +00:00
|
|
|
|
2010-06-02 11:03:30 +00:00
|
|
|
path_.end_path();
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void svg_parser::parse_line(xmlTextReaderPtr reader)
|
|
|
|
{
|
|
|
|
const xmlChar *value;
|
|
|
|
double x1 = 0.0;
|
|
|
|
double y1 = 0.0;
|
|
|
|
double x2 = 0.0;
|
|
|
|
double y2 = 0.0;
|
2012-02-02 01:53:35 +00:00
|
|
|
|
2010-05-28 16:34:34 +00:00
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "x1");
|
|
|
|
if (value) x1 = parse_double((const char*)value);
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "y1");
|
|
|
|
if (value) y1 = parse_double((const char*)value);
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "x2");
|
|
|
|
if (value) x2 = parse_double((const char*)value);
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "y2");
|
|
|
|
if (value) y2 = parse_double((const char*)value);
|
2012-02-02 01:53:35 +00:00
|
|
|
|
|
|
|
path_.begin_path();
|
2010-05-28 16:34:34 +00:00
|
|
|
parse_attr(reader);
|
|
|
|
path_.move_to(x1, y1);
|
|
|
|
path_.line_to(x2, y2);
|
|
|
|
path_.end_path();
|
2012-02-02 01:53:35 +00:00
|
|
|
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void svg_parser::parse_circle(xmlTextReaderPtr reader)
|
|
|
|
{
|
|
|
|
const xmlChar *value;
|
|
|
|
double cx = 0.0;
|
|
|
|
double cy = 0.0;
|
|
|
|
double r = 0.0;
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "cx");
|
|
|
|
if (value) cx = parse_double((const char*)value);
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "cy");
|
|
|
|
if (value) cy = parse_double((const char*)value);
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "r");
|
|
|
|
if (value) r = parse_double((const char*)value);
|
|
|
|
|
2012-02-02 01:53:35 +00:00
|
|
|
path_.begin_path();
|
2010-05-28 16:34:34 +00:00
|
|
|
parse_attr(reader);
|
2012-02-02 01:53:35 +00:00
|
|
|
|
2010-05-28 16:34:34 +00:00
|
|
|
if(r != 0.0)
|
|
|
|
{
|
2010-06-02 11:03:30 +00:00
|
|
|
if(r < 0.0) throw std::runtime_error("parse_circle: Invalid radius");
|
2010-06-13 13:45:59 +00:00
|
|
|
agg::ellipse c(cx, cy, r, r);
|
|
|
|
path_.storage().concat_path(c);
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
2012-02-02 01:53:35 +00:00
|
|
|
|
2010-05-28 16:34:34 +00:00
|
|
|
path_.end_path();
|
|
|
|
}
|
|
|
|
|
|
|
|
void svg_parser::parse_ellipse(xmlTextReaderPtr reader)
|
|
|
|
{
|
|
|
|
const xmlChar *value;
|
|
|
|
double cx = 0.0;
|
|
|
|
double cy = 0.0;
|
|
|
|
double rx = 0.0;
|
|
|
|
double ry = 0.0;
|
2012-02-02 01:53:35 +00:00
|
|
|
|
2010-05-28 16:34:34 +00:00
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "cx");
|
|
|
|
if (value) cx = parse_double((const char*)value);
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "cy");
|
|
|
|
if (value) cy = parse_double((const char*)value);
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "rx");
|
|
|
|
if (value) rx = parse_double((const char*)value);
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "ry");
|
|
|
|
if (value) ry = parse_double((const char*)value);
|
2012-02-02 01:53:35 +00:00
|
|
|
|
|
|
|
path_.begin_path();
|
2010-05-28 16:34:34 +00:00
|
|
|
parse_attr(reader);
|
2012-02-02 01:53:35 +00:00
|
|
|
|
2010-05-28 16:34:34 +00:00
|
|
|
if(rx != 0.0 && ry != 0.0)
|
|
|
|
{
|
2010-06-02 11:03:30 +00:00
|
|
|
if(rx < 0.0) throw std::runtime_error("parse_ellipse: Invalid rx");
|
|
|
|
if(ry < 0.0) throw std::runtime_error("parse_ellipse: Invalid ry");
|
2010-06-13 13:45:59 +00:00
|
|
|
agg::ellipse c(cx, cy, rx, ry);
|
|
|
|
path_.storage().concat_path(c);
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
2012-02-02 01:53:35 +00:00
|
|
|
|
2010-05-28 16:34:34 +00:00
|
|
|
path_.end_path();
|
|
|
|
}
|
|
|
|
|
|
|
|
void svg_parser::parse_rect(xmlTextReaderPtr reader)
|
|
|
|
{
|
|
|
|
const xmlChar *value;
|
|
|
|
double x = 0.0;
|
|
|
|
double y = 0.0;
|
|
|
|
double w = 0.0;
|
|
|
|
double h = 0.0;
|
|
|
|
double rx = 0.0;
|
|
|
|
double ry = 0.0;
|
2012-02-02 01:53:35 +00:00
|
|
|
|
2010-05-28 16:34:34 +00:00
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "x");
|
|
|
|
if (value) x = parse_double((const char*)value);
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "y");
|
|
|
|
if (value) y = parse_double((const char*)value);
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "width");
|
|
|
|
if (value) w = parse_double((const char*)value);
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "height");
|
|
|
|
if (value) h = parse_double((const char*)value);
|
2012-02-02 01:53:35 +00:00
|
|
|
|
2010-06-13 13:45:59 +00:00
|
|
|
bool rounded = true;
|
2010-05-28 16:34:34 +00:00
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "rx");
|
2012-02-02 01:53:35 +00:00
|
|
|
|
2010-05-28 16:34:34 +00:00
|
|
|
if (value) rx = parse_double((const char*)value);
|
2010-06-13 13:45:59 +00:00
|
|
|
else rounded = false;
|
2012-02-02 01:53:35 +00:00
|
|
|
|
2010-05-28 16:34:34 +00:00
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "ry");
|
2010-06-13 13:45:59 +00:00
|
|
|
if (value)
|
|
|
|
{
|
|
|
|
ry = parse_double((const char*)value);
|
2012-02-02 01:53:35 +00:00
|
|
|
if (!rounded)
|
2010-06-13 13:45:59 +00:00
|
|
|
{
|
|
|
|
rx = ry;
|
|
|
|
rounded = true;
|
|
|
|
}
|
|
|
|
}
|
2012-02-02 01:53:35 +00:00
|
|
|
else if (rounded)
|
2010-06-13 13:45:59 +00:00
|
|
|
{
|
|
|
|
ry = rx;
|
|
|
|
}
|
2012-02-02 01:53:35 +00:00
|
|
|
|
2010-05-28 16:34:34 +00:00
|
|
|
if(w != 0.0 && h != 0.0)
|
|
|
|
{
|
2010-06-02 11:03:30 +00:00
|
|
|
if(w < 0.0) throw std::runtime_error("parse_rect: Invalid width");
|
|
|
|
if(h < 0.0) throw std::runtime_error("parse_rect: Invalid height");
|
|
|
|
if(rx < 0.0) throw std::runtime_error("parse_rect: Invalid rx");
|
|
|
|
if(ry < 0.0) throw std::runtime_error("parse_rect: Invalid ry");
|
2012-02-02 01:53:35 +00:00
|
|
|
|
2010-06-02 11:03:30 +00:00
|
|
|
path_.begin_path();
|
|
|
|
parse_attr(reader);
|
2012-02-02 01:53:35 +00:00
|
|
|
|
2010-06-13 13:45:59 +00:00
|
|
|
if(rounded)
|
2010-06-02 11:03:30 +00:00
|
|
|
{
|
2010-06-13 13:45:59 +00:00
|
|
|
//path_.move_to(x + rx,y);
|
2012-02-02 01:53:35 +00:00
|
|
|
//path_.line_to(x + w - rx,y);
|
2010-06-13 13:45:59 +00:00
|
|
|
//path_.arc_to (rx,ry,0,0,1,x + w, y + ry);
|
|
|
|
//path_.line_to(x + w, y + h - ry);
|
|
|
|
//path_.arc_to (rx,ry,0,0,1,x + w - rx, y + h);
|
|
|
|
//path_.line_to(x + rx, y + h);
|
|
|
|
//path_.arc_to(rx,ry,0,0,1,x,y + h - ry);
|
|
|
|
//path_.line_to(x,y+ry);
|
|
|
|
//path_.arc_to(rx,ry,0,0,1,x + rx,y);
|
|
|
|
//path_.close_subpath();
|
|
|
|
agg::rounded_rect r;
|
|
|
|
r.rect(x,y,x+w,y+h);
|
|
|
|
r.radius(rx,ry);
|
|
|
|
path_.storage().concat_path(r);
|
2010-06-02 11:03:30 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
path_.move_to(x, y);
|
|
|
|
path_.line_to(x + w, y);
|
|
|
|
path_.line_to(x + w, y + h);
|
|
|
|
path_.line_to(x, y + h);
|
|
|
|
path_.close_subpath();
|
|
|
|
}
|
|
|
|
path_.end_path();
|
2010-05-28 16:34:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-26 01:18:40 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* <stop
|
2012-02-02 01:53:35 +00:00
|
|
|
style="stop-color:#ffffff;stop-opacity:1;"
|
|
|
|
offset="1"
|
|
|
|
id="stop3763" />
|
|
|
|
*/
|
2011-01-26 01:18:40 +00:00
|
|
|
void svg_parser::parse_gradient_stop(xmlTextReaderPtr reader)
|
|
|
|
{
|
|
|
|
const xmlChar *value;
|
|
|
|
|
|
|
|
double offset = 0.0;
|
|
|
|
mapnik::color stop_color;
|
|
|
|
double opacity = 1.0;
|
|
|
|
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "offset");
|
|
|
|
if (value) offset = parse_double((const char*)value);
|
|
|
|
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "style");
|
|
|
|
if (value)
|
|
|
|
{
|
|
|
|
typedef std::vector<std::pair<std::string,std::string> > cont_type;
|
|
|
|
typedef cont_type::value_type value_type;
|
|
|
|
cont_type vec;
|
|
|
|
parse_style((const char*)value, vec);
|
|
|
|
|
|
|
|
BOOST_FOREACH(value_type kv , vec )
|
|
|
|
{
|
|
|
|
if (kv.first == "stop-color")
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
mapnik::color_factory::init_from_string(stop_color,kv.second.c_str());
|
|
|
|
}
|
|
|
|
catch (mapnik::config_error & ex)
|
|
|
|
{
|
|
|
|
std::cerr << ex.what() << std::endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (kv.first == "stop-opacity")
|
|
|
|
{
|
|
|
|
opacity = parse_double(kv.second.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "stop-color");
|
|
|
|
if (value)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
mapnik::color_factory::init_from_string(stop_color,(const char *) value);
|
|
|
|
}
|
|
|
|
catch (mapnik::config_error & ex)
|
|
|
|
{
|
|
|
|
std::cerr << ex.what() << std::endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "stop-opacity");
|
|
|
|
if (value)
|
|
|
|
{
|
|
|
|
opacity = parse_double((const char *) value);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
stop_color.set_alpha(opacity*255);
|
|
|
|
|
|
|
|
temporary_gradient_.second.add_stop(offset, stop_color);
|
|
|
|
|
2011-01-26 09:17:01 +00:00
|
|
|
//std::cerr << "\tFound Stop: " << offset << " " << (unsigned)stop_color.red() << " " << (unsigned)stop_color.green() << " " << (unsigned)stop_color.blue() << " " << (unsigned)stop_color.alpha() << std::endl;
|
2011-01-26 01:18:40 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
bool svg_parser::parse_common_gradient(xmlTextReaderPtr reader)
|
|
|
|
{
|
|
|
|
const xmlChar *value;
|
|
|
|
|
|
|
|
std::string id;
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "id");
|
|
|
|
if (value)
|
|
|
|
{
|
|
|
|
// start a new gradient
|
|
|
|
gradient new_grad;
|
|
|
|
id = std::string((const char *) value);
|
|
|
|
temporary_gradient_ = std::make_pair(id, new_grad);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// no point without an ID
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if we should inherit from another tag
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "xlink:href");
|
|
|
|
if (value && value[0] == '#')
|
|
|
|
{
|
|
|
|
std::string linkid = (const char *) &value[1];
|
|
|
|
if (gradient_map_.count(linkid))
|
|
|
|
{
|
2011-01-26 09:17:01 +00:00
|
|
|
//std::cerr << "\tLoading linked gradient properties from " << linkid << std::endl;
|
2011-01-26 01:18:40 +00:00
|
|
|
temporary_gradient_.second = gradient_map_[linkid];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::cerr << "Failed to find linked gradient " << linkid << std::endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "gradientUnits");
|
|
|
|
if (value && std::string((const char*) value) == "userSpaceOnUse")
|
|
|
|
{
|
|
|
|
temporary_gradient_.second.set_units(USER_SPACE_ON_USE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
temporary_gradient_.second.set_units(OBJECT_BOUNDING_BOX);
|
|
|
|
}
|
|
|
|
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "gradientTransform");
|
|
|
|
if (value)
|
|
|
|
{
|
|
|
|
agg::trans_affine tr;
|
|
|
|
mapnik::svg::parse_transform((const char*) value,tr);
|
|
|
|
temporary_gradient_.second.set_transform(tr);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* <radialGradient
|
2012-02-02 01:53:35 +00:00
|
|
|
collect="always"
|
|
|
|
xlink:href="#linearGradient3759"
|
|
|
|
id="radialGradient3765"
|
|
|
|
cx="-1.2957155"
|
|
|
|
cy="-21.425594"
|
|
|
|
fx="-1.2957155"
|
|
|
|
fy="-21.425594"
|
|
|
|
r="5.1999998"
|
|
|
|
gradientUnits="userSpaceOnUse" />
|
|
|
|
*/
|
2011-01-26 01:18:40 +00:00
|
|
|
void svg_parser::parse_radial_gradient(xmlTextReaderPtr reader)
|
|
|
|
{
|
|
|
|
if (!parse_common_gradient(reader))
|
|
|
|
return;
|
|
|
|
|
|
|
|
const xmlChar *value;
|
|
|
|
double cx = 0.5;
|
|
|
|
double cy = 0.5;
|
|
|
|
double fx = 0.0;
|
|
|
|
double fy = 0.0;
|
|
|
|
double r = 0.5;
|
|
|
|
bool has_percent=true;
|
|
|
|
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "cx");
|
|
|
|
if (value) cx = parse_double_optional_percent((const char*)value, has_percent);
|
|
|
|
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "cy");
|
|
|
|
if (value) cy = parse_double_optional_percent((const char*)value, has_percent);
|
|
|
|
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "fx");
|
|
|
|
if (value)
|
|
|
|
fx = parse_double_optional_percent((const char*)value, has_percent);
|
|
|
|
else
|
|
|
|
fx = cx;
|
|
|
|
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "fy");
|
|
|
|
if (value)
|
|
|
|
fy = parse_double_optional_percent((const char*)value, has_percent);
|
|
|
|
else
|
|
|
|
fy = cy;
|
|
|
|
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "r");
|
|
|
|
if (value) r = parse_double_optional_percent((const char*)value, has_percent);
|
|
|
|
|
|
|
|
// this logic for detecting %'s will not support mixed coordinates.
|
|
|
|
if (has_percent && temporary_gradient_.second.get_units() == USER_SPACE_ON_USE)
|
|
|
|
{
|
|
|
|
temporary_gradient_.second.set_units(USER_SPACE_ON_USE_BOUNDING_BOX);
|
|
|
|
}
|
|
|
|
|
|
|
|
temporary_gradient_.second.set_gradient_type(RADIAL);
|
|
|
|
temporary_gradient_.second.set_control_points(fx,fy,cx,cy,r);
|
|
|
|
// add this here in case we have no end tag, will be replaced if we do
|
|
|
|
gradient_map_[temporary_gradient_.first] = temporary_gradient_.second;
|
|
|
|
|
2011-01-26 09:17:01 +00:00
|
|
|
//std::cerr << "Found Radial Gradient: " << " " << cx << " " << cy << " " << fx << " " << fy << " " << r << std::endl;
|
2011-01-26 01:18:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void svg_parser::parse_linear_gradient(xmlTextReaderPtr reader)
|
|
|
|
{
|
|
|
|
if (!parse_common_gradient(reader))
|
|
|
|
return;
|
|
|
|
|
|
|
|
const xmlChar *value;
|
|
|
|
double x1 = 0.0;
|
|
|
|
double x2 = 1.0;
|
|
|
|
double y1 = 0.0;
|
|
|
|
double y2 = 1.0;
|
|
|
|
|
|
|
|
bool has_percent=true;
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "x1");
|
|
|
|
if (value) x1 = parse_double_optional_percent((const char*)value, has_percent);
|
|
|
|
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "x2");
|
|
|
|
if (value) x2 = parse_double_optional_percent((const char*)value, has_percent);
|
|
|
|
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "y1");
|
|
|
|
if (value) y1 = parse_double_optional_percent((const char*)value, has_percent);
|
|
|
|
|
|
|
|
value = xmlTextReaderGetAttribute(reader, BAD_CAST "y2");
|
|
|
|
if (value) y2 = parse_double_optional_percent((const char*)value, has_percent);
|
|
|
|
|
|
|
|
// this logic for detecting %'s will not support mixed coordinates.
|
|
|
|
if (has_percent && temporary_gradient_.second.get_units() == USER_SPACE_ON_USE)
|
|
|
|
{
|
|
|
|
temporary_gradient_.second.set_units(USER_SPACE_ON_USE_BOUNDING_BOX);
|
|
|
|
}
|
|
|
|
|
|
|
|
temporary_gradient_.second.set_gradient_type(LINEAR);
|
|
|
|
temporary_gradient_.second.set_control_points(x1,y1,x2,y2);
|
|
|
|
// add this here in case we have no end tag, will be replaced if we do
|
|
|
|
gradient_map_[temporary_gradient_.first] = temporary_gradient_.second;
|
|
|
|
|
2011-01-26 09:17:01 +00:00
|
|
|
//std::cerr << "Found Linear Gradient: " << "(" << x1 << " " << y1 << "),(" << x2 << " " << y2 << ")" << std::endl;
|
2011-01-26 01:18:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void svg_parser::parse_pattern(xmlTextReaderPtr reader)
|
|
|
|
{
|
|
|
|
//const xmlChar *value;
|
|
|
|
}
|
|
|
|
|
2010-05-28 16:34:34 +00:00
|
|
|
}}
|