Skip to content

Commit

Permalink
Fix handling of text content inside <a> element which is a child of <…
Browse files Browse the repository at this point in the history
…text> element
  • Loading branch information
0legmax committed Jun 14, 2016
1 parent ebaf2fd commit 2b549f6
Show file tree
Hide file tree
Showing 6 changed files with 221 additions and 16 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
SVG++ library 1.2
SVG++ library 1.2.1
=======================

Please visit http://svgpp.org/ for description, tutorial and reference.
Expand Down Expand Up @@ -44,8 +44,9 @@ Take a look at [Tutorial](http://svgpp.org/lesson01.html) to get the idea about

####What's new####

SVG++ update 1.2 focuses on reducing compiler memory usage by allowing separation
of template heavy Boost.Spirit code to other compilation unit.
* SVG++ update 1.2.1 fixes handling text content inside `<a>` element that is a child of `<text>` element.
* SVG++ update 1.2 focuses on reducing compiler memory usage by allowing separation
of template heavy Boost.Spirit code to other compilation unit.

####How to Help####

Expand Down
3 changes: 3 additions & 0 deletions include/svgpp/definitions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,7 @@ namespace length_dimension

struct iri_fragment {}; // Prefixes fragment part of IRI in arguments list

struct text_content; // Used in child element sequences together with tag::element::* tags to
// mark elements that should handle character data content

}} // namespace tag
7 changes: 3 additions & 4 deletions include/svgpp/document_traversal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
#include <svgpp/policy/document_traversal_control.hpp>
#include <svgpp/policy/text_events.hpp>
#include <svgpp/traits/child_element_types.hpp>
#include <svgpp/traits/element_with_text_content.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/parameter.hpp>
#include <boost/static_assert.hpp>
Expand Down Expand Up @@ -191,7 +190,7 @@ class document_traversal
static typename boost::enable_if<
boost::mpl::and_<
boost::mpl::empty<typename traits::child_element_types<ElementTag>::type>,
boost::mpl::not_<traits::element_with_text_content<ElementTag> > >, bool>::type
boost::mpl::not_<boost::mpl::has_key<ExpectedChildElements, tag::text_content> > >, bool>::type
load_element_content(XMLElement const &, Context const &, ElementTag)
{
return true;
Expand All @@ -201,7 +200,7 @@ class document_traversal
static typename boost::disable_if<
boost::mpl::or_<
boost::mpl::empty<typename traits::child_element_types<ElementTag>::type>,
traits::element_with_text_content<ElementTag> >, bool>::type
boost::mpl::has_key<ExpectedChildElements, tag::text_content> >, bool>::type
load_element_content(XMLElement const & xml_element, Context & context, ElementTag element_tag)
{
typedef typename boost::parameter::value_type<args, tag::xml_element_policy,
Expand All @@ -224,7 +223,7 @@ class document_traversal
}

template<class ExpectedChildElements, class XMLElement, class Context, class ElementTag>
static typename boost::enable_if<traits::element_with_text_content<ElementTag>, bool>::type
static typename boost::enable_if<boost::mpl::has_key<ExpectedChildElements, tag::text_content>, bool>::type
load_element_content(XMLElement const & xml_element, Context & context, ElementTag element_tag)
{
typedef typename boost::parameter::value_type<args, tag::xml_element_policy,
Expand Down
26 changes: 18 additions & 8 deletions include/svgpp/traits/child_element_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,24 @@ template<class ElementTag, class Enable = void>
struct child_element_types;

template<class ElementTag>
struct child_element_types<ElementTag, typename boost::enable_if<boost::mpl::has_key<boost::mpl::set16<
tag::element::altGlyph, tag::element::foreignObject, tag::element::script, tag::element::style, // Actually "Any elements or character data"
tag::element::glyphRef, tag::element::desc, tag::element::hkern, tag::element::title, tag::element::font_face_format,
tag::element::font_face_name, tag::element::metadata, tag::element::vkern,
struct child_element_types<ElementTag, typename boost::enable_if<boost::mpl::has_key<boost::mpl::set10<
tag::element::foreignObject, tag::element::glyphRef, tag::element::hkern, tag::element::font_face_format,
tag::element::font_face_name, tag::element::vkern,
tag::element::feFuncA, tag::element::feFuncR, tag::element::feFuncG, tag::element::feFuncB
>, ElementTag> >::type>
{
typedef boost::mpl::set0<> type;
};

template<class ElementTag>
struct child_element_types<ElementTag, typename boost::enable_if<boost::mpl::has_key<boost::mpl::set6<
tag::element::altGlyph, tag::element::desc, tag::element::metadata,
tag::element::script, tag::element::style, tag::element::title
>, ElementTag> >::type>
{
typedef boost::mpl::set1<tag::text_content> type;
};

template<class ElementTag>
struct child_element_types<ElementTag, typename boost::enable_if<boost::mpl::has_key<boost::mpl::set10<
tag::element::svg, tag::element::g, tag::element::glyph, tag::element::a, tag::element::defs, tag::element::symbol,
Expand Down Expand Up @@ -96,12 +104,13 @@ struct child_element_types<tag::element::text, void>
boost::mpl::joint_view<
traits::animation_elements,
traits::descriptive_elements>,
boost::mpl::set5<
boost::mpl::set6<
tag::element::altGlyph,
tag::element::textPath,
tag::element::tref,
tag::element::tspan,
tag::element::a
tag::element::a,
tag::text_content
>,
boost::mpl::insert<boost::mpl::_1, boost::mpl::_2>
>::type type;
Expand All @@ -114,14 +123,15 @@ struct child_element_types<ElementTag, typename boost::enable_if<boost::mpl::has
typedef
boost::mpl::fold<
traits::descriptive_elements,
boost::mpl::set7<
boost::mpl::set8<
tag::element::a,
tag::element::altGlyph,
tag::element::animate,
tag::element::animateColor,
tag::element::set,
tag::element::tref,
tag::element::tspan
tag::element::tspan,
tag::text_content
>,
boost::mpl::insert<boost::mpl::_1, boost::mpl::_2>
>::type type;
Expand Down
3 changes: 2 additions & 1 deletion src/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ add_executable( ParserGTest
dictionary_test.cpp
attribute_traversal_test.cpp
css_style_iterator_test.cpp
clock_value_grammar_test.cpp
clock_value_grammar_test.cpp
document_traversal_a_test.cpp
icc_color_grammar_test.cpp
length_factory_test.cpp
list_of_points_test.cpp
Expand Down
191 changes: 191 additions & 0 deletions src/test/document_traversal_a_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
#include <svgpp/document_traversal.hpp>
#include <rapidxml_ns/rapidxml_ns.hpp>
#include <svgpp/policy/xml/rapidxml_ns.hpp>
#include <sstream>

#include <gtest/gtest.h>

#define TEXT(x) #x

namespace
{
struct AInSvgContext;

struct SvgContext
{
SvgContext(std::ostringstream & log): log(log) { log << "<svg>"; }
SvgContext(SvgContext & parent): log(parent.log) { log << "<svg>"; }
SvgContext(AInSvgContext & parent);

void on_exit_element() {}

std::ostringstream & log;
};

struct AInSvgContext
{
AInSvgContext(SvgContext & parent) : log(parent.log) { log << "<aInSvg>"; }
void on_exit_element() {}

std::ostringstream & log;
};

struct RectContext
{
RectContext(SvgContext & parent) { parent.log << "<rect>"; }
RectContext(AInSvgContext & parent) { parent.log << "<rect>"; }
void on_exit_element() {}
};

struct EllipseContext
{
EllipseContext(SvgContext & parent) { parent.log << "<ellipse>"; }
EllipseContext(AInSvgContext & parent) { parent.log << "<rect>"; }
void on_exit_element() {}
};

struct TextContext
{
TextContext(SvgContext & parent): log(parent.log) { parent.log << "<text>"; }
TextContext(AInSvgContext & parent): log(parent.log) { parent.log << "<text>"; }

void on_exit_element() {}
template<class TextRange>
void set_text(TextRange const & text) { log << std::string(boost::begin(text), boost::end(text));}

std::ostringstream & log;
};

struct AInTextContext
{
AInTextContext(TextContext & parent): log(parent.log) { parent.log << "<aInText>"; }

void on_exit_element() {}
template<class TextRange>
void set_text(TextRange const & text) { log << std::string(boost::begin(text), boost::end(text)); }

std::ostringstream & log;
};

SvgContext::SvgContext(AInSvgContext & parent): log(parent.log) { log << "<svg>"; }

struct context_factories
{
template<class ParentContext, class ElementTag>
struct apply;
};

template<>
struct context_factories::apply<SvgContext, svgpp::tag::element::svg>
{
typedef svgpp::factory::context::on_stack<SvgContext> type;
};

template<>
struct context_factories::apply<SvgContext, svgpp::tag::element::rect>
{
typedef svgpp::factory::context::on_stack<RectContext> type;
};

template<>
struct context_factories::apply<SvgContext, svgpp::tag::element::ellipse>
{
typedef svgpp::factory::context::on_stack<EllipseContext> type;
};

template<>
struct context_factories::apply<SvgContext, svgpp::tag::element::text>
{
typedef svgpp::factory::context::on_stack<TextContext> type;
};

template<>
struct context_factories::apply<SvgContext, svgpp::tag::element::a>
{
typedef svgpp::factory::context::on_stack<AInSvgContext> type;
};

template<>
struct context_factories::apply<AInSvgContext, svgpp::tag::element::svg>
{
typedef svgpp::factory::context::on_stack<SvgContext> type;
};

template<>
struct context_factories::apply<AInSvgContext, svgpp::tag::element::rect>
{
typedef svgpp::factory::context::on_stack<RectContext> type;
};

template<>
struct context_factories::apply<AInSvgContext, svgpp::tag::element::ellipse>
{
typedef svgpp::factory::context::on_stack<EllipseContext> type;
};

template<>
struct context_factories::apply<AInSvgContext, svgpp::tag::element::text>
{
typedef svgpp::factory::context::on_stack<TextContext> type;
};

template<>
struct context_factories::apply<TextContext, svgpp::tag::element::a>
{
typedef svgpp::factory::context::on_stack<AInTextContext> type;
};

char const xml1[] =
TEXT(<svg xmlns = "http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">)
TEXT(<rect x=".01" y=".01" width="4.98" height="2.98" fill="none" stroke="blue" stroke-width=".03"/>)
TEXT(<a xlink:href="http://www.w3.org">)
TEXT( <ellipse cx="2.5" cy="1.5" rx="2" ry="1" fill="red"/>)
TEXT(</a>)
TEXT(<text x="1" y="2">)
TEXT( <a xlink:href="http://www.w3.org">)
TEXT( Some text string)
TEXT( </a>)
TEXT(</text>)
TEXT(</svg>)
;
}

TEST(DocumentTraversal, a)
{
std::vector<char> modified_xml(xml1, xml1 + strlen(xml1) + 1);
rapidxml_ns::xml_document<char> doc;
doc.parse<0>(&modified_xml[0]);
rapidxml_ns::xml_node<char> const * svg_element = doc.first_node();
ASSERT_TRUE(svg_element != NULL);
std::ostringstream str;
SvgContext context(str);
EXPECT_TRUE((
svgpp::document_traversal<
svgpp::processed_elements<
boost::mpl::set<
svgpp::tag::element::svg,
svgpp::tag::element::ellipse,
svgpp::tag::element::rect,
svgpp::tag::element::text,
svgpp::tag::element::a
>::type
>,
svgpp::processed_attributes<
boost::mpl::set<>::type
>,
svgpp::context_factories<context_factories>,
svgpp::basic_shapes_policy<svgpp::policy::basic_shapes::raw>
>::load_document(svg_element, context)));

std::ostringstream expected_str;
{
SvgContext c1(expected_str);
RectContext c2(c1);
AInSvgContext c3(c1);
EllipseContext c4(c3);
TextContext c5(c1);
AInTextContext c6(c5);
c6.set_text(std::string("Some text string"));
}
EXPECT_EQ(expected_str.str(), str.str());
}

0 comments on commit 2b549f6

Please sign in to comment.