summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJabiertxof <jtx@jtx>2016-12-28 08:42:19 +0000
committerJabiertxof <jtx@jtx>2016-12-28 08:42:19 +0000
commit50714133d92da517b5185385ea4c553408c80e54 (patch)
tree27ecbc256d57c51e4425bb566da888b825941c10
parentFixes bug #1652465 on mirror and copy rotate LPE. (diff)
downloadinkscape-50714133d92da517b5185385ea4c553408c80e54.tar.gz
inkscape-50714133d92da517b5185385ea4c553408c80e54.zip
First attemp working
(bzr r15356.1.1)
-rw-r--r--src/live_effects/CMakeLists.txt6
-rw-r--r--src/live_effects/effect.cpp10
-rw-r--r--src/live_effects/effect.h1
-rw-r--r--src/live_effects/lpe-clone-original.cpp169
-rw-r--r--src/live_effects/lpe-clone-original.h20
-rw-r--r--src/sp-ellipse.cpp13
-rw-r--r--src/sp-item-group.cpp37
-rw-r--r--src/sp-lpe-item.cpp21
-rw-r--r--src/sp-lpe-item.h3
-rw-r--r--src/sp-object.cpp14
-rw-r--r--src/sp-object.h3
-rw-r--r--src/sp-path.cpp4
-rw-r--r--src/ui/clipboard.cpp2
13 files changed, 261 insertions, 42 deletions
diff --git a/src/live_effects/CMakeLists.txt b/src/live_effects/CMakeLists.txt
index 784317090..5ffccc7c0 100644
--- a/src/live_effects/CMakeLists.txt
+++ b/src/live_effects/CMakeLists.txt
@@ -60,6 +60,9 @@ set(live_effects_SRC
parameter/array.cpp
parameter/bool.cpp
parameter/filletchamferpointarray.cpp
+ parameter/item-reference.cpp
+ parameter/item.cpp
+ parameter/originalitem.cpp
parameter/originalpath.cpp
parameter/originalpatharray.cpp
parameter/parameter.cpp
@@ -142,6 +145,9 @@ set(live_effects_SRC
parameter/bool.h
parameter/enum.h
parameter/filletchamferpointarray.h
+ parameter/item.h
+ parameter/item-reference.h
+ parameter/originalitem.h
parameter/originalpath.h
parameter/originalpatharray.h
parameter/parameter.h
diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp
index 5cc0d6f20..cfd393b87 100644
--- a/src/live_effects/effect.cpp
+++ b/src/live_effects/effect.cpp
@@ -116,7 +116,7 @@ const Util::EnumData<EffectType> LPETypeData[] = {
{RULER, N_("Ruler"), "ruler"},
/* 0.91 */
{POWERSTROKE, N_("Power stroke"), "powerstroke"},
- {CLONE_ORIGINAL, N_("Clone original path"), "clone_original"},
+ {CLONE_ORIGINAL, N_("Clone original"), "clone_original"},
/* 0.92 */
{SIMPLIFY, N_("Simplify"), "simplify"},
{LATTICE2, N_("Lattice Deformation 2"), "lattice2"},
@@ -457,8 +457,7 @@ void Effect::doBeforeEffect_impl(SPLPEItem const* lpeitem)
//printf("(SPLPEITEM*) %p\n", sp_lpe_item);
SPShape * shape = dynamic_cast<SPShape *>(sp_lpe_item);
if(shape){
- sp_curve = shape->getCurve();
- pathvector_before_effect = sp_curve->get_pathvector();
+ setSPCurve(shape->getCurve());
}
doBeforeEffect(lpeitem);
if (apply_to_clippath_and_mask && SP_IS_GROUP(sp_lpe_item)) {
@@ -468,6 +467,11 @@ void Effect::doBeforeEffect_impl(SPLPEItem const* lpeitem)
update_helperpath();
}
+void Effect::setSPCurve(SPCurve *curve)
+{
+ sp_curve = curve;
+ pathvector_before_effect = curve->get_pathvector();
+}
/**
* Effects can have a parameter path set before they are applied by accepting a nonzero number of
* mouse clicks. This method activates the pen context, which waits for the specified number of
diff --git a/src/live_effects/effect.h b/src/live_effects/effect.h
index 1997ff0ca..e975deb05 100644
--- a/src/live_effects/effect.h
+++ b/src/live_effects/effect.h
@@ -73,6 +73,7 @@ public:
static int acceptsNumClicks(EffectType type);
int acceptsNumClicks() const { return acceptsNumClicks(effectType()); }
void doAcceptPathPreparations(SPLPEItem *lpeitem);
+ void setSPCurve(SPCurve *curve);
/*
* isReady() indicates whether all preparations which are necessary to apply the LPE are done,
diff --git a/src/live_effects/lpe-clone-original.cpp b/src/live_effects/lpe-clone-original.cpp
index 10418a02d..be8bc9e0d 100644
--- a/src/live_effects/lpe-clone-original.cpp
+++ b/src/live_effects/lpe-clone-original.cpp
@@ -6,6 +6,11 @@
#include "live_effects/lpe-clone-original.h"
#include "display/curve.h"
+#include "svg/path-string.h"
+#include "svg/svg.h"
+#include <boost/algorithm/string.hpp>
+#include "xml/sp-css-attr.h"
+
// TODO due to internal breakage in glibmm headers, this must be last:
#include <glibmm/i18n.h>
@@ -14,9 +19,148 @@ namespace LivePathEffect {
LPECloneOriginal::LPECloneOriginal(LivePathEffectObject *lpeobject) :
Effect(lpeobject),
- linked_path(_("Linked path:"), _("Path from which to take the original path data"), "linkedpath", &wr, this)
+ linked_item(_("Linked Item:"), _("Item from which to take the original data"), "linked_item", &wr, this),
+ preserve_position(_("Preserve position"), _("Preserve position"), "preserve_position", &wr, this, false),
+ attributes("Attributes linked", "Attributes linked", "attributes", &wr, this,""),
+ style_attributes("Style attributes linked", "Style attributes linked", "style_attributes", &wr, this,"")
{
- registerParameter( dynamic_cast<Parameter *>(&linked_path) );
+ registerParameter(&linked_item);
+ registerParameter(&attributes);
+ registerParameter(&style_attributes);
+ registerParameter(&preserve_position);
+ attributes.param_hide_canvas_text();
+ style_attributes.param_hide_canvas_text();
+ apply_to_clippath_and_mask = true;
+}
+
+void
+LPECloneOriginal::cloneAttrbutes(SPObject *origin, SPObject *dest, bool live, const char * first_attribute, ...)
+{
+ va_list args;
+ va_start(args, first_attribute);
+ SPDocument * document = SP_ACTIVE_DOCUMENT;
+ if ( SP_IS_GROUP(origin) && SP_IS_GROUP(dest) && SP_GROUP(origin)->getItemCount() == SP_GROUP(dest)->getItemCount() ) {
+ std::vector< SPObject * > childs = origin->childList(true);
+ size_t index = 0;
+ for (std::vector<SPObject * >::iterator obj_it = childs.begin();
+ obj_it != childs.end(); ++obj_it) {
+ SPObject *dest_child = dest->nthChild(index);
+ cloneAttrbutes(*obj_it, dest_child, live, first_attribute, args);
+ index++;
+ }
+ }
+ SPShape * shape_origin = SP_SHAPE(origin);
+ SPShape * shape_dest = SP_SHAPE(dest);
+ for (const char* att = first_attribute; att != NULL; att = va_arg(args, const char*)) {
+ std::vector<std::string> elems;
+ boost::split(elems, att, boost::is_any_of(","));
+ for (std::vector<std::string>::const_iterator atts = elems.begin();
+ atts != elems.end(); ++atts) {
+ const char* attribute = (*atts).c_str();
+ if ( std::strcmp(attribute, "transform") == 0 ) {
+ Geom::Affine affine_dest = Geom::identity();
+ sp_svg_transform_read(SP_ITEM(dest)->getAttribute("transform"), &affine_dest);
+ dest->getRepr()->setAttribute(attribute, origin->getRepr()->attribute(attribute));
+ if (preserve_position) {
+ Geom::Affine affine_origin = Geom::identity();
+ sp_svg_transform_read(SP_ITEM(origin)->getAttribute("transform"), &affine_origin);
+ SP_ITEM(dest)->transform = Geom::Translate(affine_dest.translation()) * Geom::Translate(affine_origin.translation()).inverse() * affine_origin;
+ }
+ } else if ( shape_dest && shape_origin && live && (std::strcmp(attribute, "d") == 0 || std::strcmp(attribute, "inkscape:original-d") == 0)) {
+ SPCurve *c = NULL;
+ if (std::strcmp(attribute, "d") == 0) {
+ c = shape_origin->getCurve();
+ } else {
+ c = shape_origin->getCurveBeforeLPE();
+ }
+ if (c) {
+ Geom::PathVector c_pv = c->get_pathvector();
+ if (preserve_position) {
+ Geom::OptRect orig_bbox = SP_ITEM(origin)->geometricBounds();
+ if (orig_bbox) {
+ c_pv *= Geom::Translate(Geom::Point(boundingbox_X.min(), boundingbox_Y.min()) - (*orig_bbox).corner(0));
+ }
+ }
+ c->set_pathvector(c_pv);
+ shape_dest->setCurveInsync(c, TRUE);
+ dest->getRepr()->setAttribute(attribute, sp_svg_write_path(c_pv));
+ c->unref();
+ } else {
+ dest->getRepr()->setAttribute(attribute, NULL);
+ }
+ } else {
+ dest->getRepr()->setAttribute(attribute, origin->getRepr()->attribute(attribute));
+ }
+ }
+ }
+ va_end(args);
+}
+
+void
+LPECloneOriginal::cloneStyleAttrbutes(SPObject *origin, SPObject *dest, const char * first_attribute, ...)
+{
+ va_list args;
+ va_start(args, first_attribute);
+
+ if ( SP_IS_GROUP(origin) && SP_IS_GROUP(dest) && SP_GROUP(origin)->getItemCount() == SP_GROUP(dest)->getItemCount() ) {
+ std::vector< SPObject * > childs = origin->childList(true);
+ size_t index = 0;
+ for (std::vector<SPObject * >::iterator obj_it = childs.begin();
+ obj_it != childs.end(); ++obj_it) {
+ SPObject *dest_child = dest->nthChild(index);
+ cloneStyleAttrbutes(*obj_it, dest_child, first_attribute, args);
+ index++;
+ }
+ }
+ SPCSSAttr *css_origin = sp_repr_css_attr_new();
+ sp_repr_css_attr_add_from_string(css_origin, origin->getRepr()->attribute("style"));
+ SPCSSAttr *css_dest = sp_repr_css_attr_new();
+ sp_repr_css_attr_add_from_string(css_dest, dest->getRepr()->attribute("style"));
+ for (const char* att = first_attribute; att != NULL; att = va_arg(args, const char*)) {
+ std::vector<std::string> elems;
+ boost::split(elems, att, boost::is_any_of(","));
+ for (std::vector<std::string>::const_iterator atts = elems.begin();
+ atts != elems.end(); ++atts) {
+ const char* attribute = (*atts).c_str();
+ const char* origin_attribute = sp_repr_css_property(css_origin, attribute, "");
+ if (origin_attribute == "") {
+ sp_repr_css_set_property (css_dest, attribute, NULL);
+ } else {
+ sp_repr_css_set_property (css_dest, attribute, origin_attribute);
+ }
+ }
+ Glib::ustring css_str;
+ sp_repr_css_write_string(css_dest,css_str);
+ dest->getRepr()->setAttribute("style", css_str.c_str());
+ }
+ va_end(args);
+}
+
+void
+LPECloneOriginal::doBeforeEffect (SPLPEItem const* lpeitem){
+ original_bbox(lpeitem);
+ if (linked_item.linksToItem() && sp_lpe_item) {
+ cloneAttrbutes(linked_item.getObject(), SP_OBJECT(sp_lpe_item), true, attributes.param_getSVGValue(), NULL); //NULL required
+ cloneStyleAttrbutes(linked_item.getObject(), SP_OBJECT(sp_lpe_item), style_attributes.param_getSVGValue(), NULL); //NULL required
+ SPShape * shape = dynamic_cast<SPShape *>(sp_lpe_item);
+ if(shape){
+ this->setSPCurve(shape->getCurve());
+ }
+ }
+}
+
+void
+LPECloneOriginal::doOnApply(SPLPEItem const* lpeitem){
+ Glib::ustring attributes_value("d,transform");
+ attributes.param_setValue(attributes_value);
+ attributes.write_to_SVG();
+ Glib::ustring style_attributes_value("opacity,border-width");
+ style_attributes.param_setValue(style_attributes_value);
+ style_attributes.write_to_SVG();
+}
+
+void
+LPECloneOriginal::doAfterEffect (SPLPEItem const* lpeitem){
}
LPECloneOriginal::~LPECloneOriginal()
@@ -26,12 +170,21 @@ LPECloneOriginal::~LPECloneOriginal()
void LPECloneOriginal::doEffect (SPCurve * curve)
{
- if ( linked_path.linksToPath() ) {
- Geom::PathVector linked_pathv = linked_path.get_pathvector();
- if ( !linked_pathv.empty() ) {
- curve->set_pathvector(linked_pathv);
- }
- }
+// std::vector<std::string> elems;
+// const char * attrs = attributes.param_getSVGValue();
+// boost::split(elems, attrs, boost::is_any_of(","));
+// bool has_d = false;
+// for (std::vector<std::string>::const_iterator atts = elems.begin();
+// atts != elems.end(); ++atts) {
+// const char* attribute = (*atts).c_str();
+// if (std::strcmp(attribute, "d") == 0) {
+// has_d = true;
+// }
+// }
+// if (linked_item.linksToItem() && has_d) {
+// curve->reset();
+// }
+ curve->set_pathvector(pathvector_before_effect);
}
} // namespace LivePathEffect
diff --git a/src/live_effects/lpe-clone-original.h b/src/live_effects/lpe-clone-original.h
index abf65ded8..148590695 100644
--- a/src/live_effects/lpe-clone-original.h
+++ b/src/live_effects/lpe-clone-original.h
@@ -10,22 +10,32 @@
*/
#include "live_effects/effect.h"
-#include "live_effects/parameter/originalpath.h"
+#include "live_effects/parameter/originalitem.h"
+#include "live_effects/parameter/parameter.h"
+#include "live_effects/parameter/point.h"
+#include "live_effects/parameter/text.h"
+#include "live_effects/lpegroupbbox.h"
namespace Inkscape {
namespace LivePathEffect {
-class LPECloneOriginal : public Effect {
+class LPECloneOriginal : public Effect, GroupBBoxEffect {
public:
LPECloneOriginal(LivePathEffectObject *lpeobject);
virtual ~LPECloneOriginal();
virtual void doEffect (SPCurve * curve);
+ virtual void doBeforeEffect (SPLPEItem const* lpeitem);
+ virtual void doAfterEffect (SPLPEItem const* lpeitem);
+ virtual void doOnApply(SPLPEItem const* lpeitem);
+ void cloneAttrbutes(SPObject *origin, SPObject *dest, bool live, const char * first_attribute, ...);
+ void cloneStyleAttrbutes(SPObject *origin, SPObject *dest, const char * first_attribute, ...);
private:
- OriginalPathParam linked_path;
-
-private:
+ OriginalItemParam linked_item;
+ BoolParam preserve_position;
+ TextParam attributes;
+ TextParam style_attributes;
LPECloneOriginal(const LPECloneOriginal&);
LPECloneOriginal& operator=(const LPECloneOriginal&);
};
diff --git a/src/sp-ellipse.cpp b/src/sp-ellipse.cpp
index ed1e2b504..9589d6fce 100644
--- a/src/sp-ellipse.cpp
+++ b/src/sp-ellipse.cpp
@@ -445,11 +445,20 @@ void SPGenericEllipse::set_shape()
if (hasPathEffect() && pathEffectsEnabled()) {
SPCurve *c_lpe = curve->copy();
bool success = this->performPathEffect(c_lpe);
-
+
if (success) {
this->setCurveInsync(c_lpe, TRUE);
+ } else {
+ Inkscape::XML::Node *repr = this->getRepr();
+ if (gchar const * value = repr->attribute("d")) {
+ Geom::PathVector pv = sp_svg_read_pathv(value);
+ SPCurve *oldcurve = new (std::nothrow) SPCurve(pv);
+ if (oldcurve) {
+ this->setCurveInsync(oldcurve, TRUE);
+ oldcurve->unref();
+ }
+ }
}
-
c_lpe->unref();
}
diff --git a/src/sp-item-group.cpp b/src/sp-item-group.cpp
index 7b2507b5e..808d475c7 100644
--- a/src/sp-item-group.cpp
+++ b/src/sp-item-group.cpp
@@ -950,25 +950,36 @@ sp_group_perform_patheffect(SPGroup *group, SPGroup *topgroup, bool write)
} else {
c = subShape->getCurve();
}
-
+ bool success = false;
// only run LPEs when the shape has a curve defined
if (c) {
c->transform(i2anc_affine(subitem, topgroup));
- topgroup->performPathEffect(c);
+ success = topgroup->performPathEffect(c, subShape);
c->transform(i2anc_affine(subitem, topgroup).inverse());
- subShape->setCurve(c, TRUE);
-
- if (write) {
+ if (c && success) {
+ subShape->setCurve(c, TRUE);
+ if (write) {
+ Inkscape::XML::Node *repr = subitem->getRepr();
+ gchar *str = sp_svg_write_path(c->get_pathvector());
+ repr->setAttribute("d", str);
+ #ifdef GROUP_VERBOSE
+ g_message("sp_group_perform_patheffect writes 'd' attribute");
+ #endif
+ g_free(str);
+ }
+ c->unref();
+ } else {
+ // LPE was unsuccesfull or doeffect stack return null. Read the old 'd'-attribute.
Inkscape::XML::Node *repr = subitem->getRepr();
- gchar *str = sp_svg_write_path(c->get_pathvector());
- repr->setAttribute("d", str);
-#ifdef GROUP_VERBOSE
- g_message("sp_group_perform_patheffect writes 'd' attribute");
-#endif
- g_free(str);
+ if (gchar const * value = repr->attribute("d")) {
+ Geom::PathVector pv = sp_svg_read_pathv(value);
+ SPCurve *oldcurve = new (std::nothrow) SPCurve(pv);
+ if (oldcurve) {
+ subShape->setCurve(oldcurve, TRUE);
+ oldcurve->unref();
+ }
+ }
}
-
- c->unref();
}
}
}
diff --git a/src/sp-lpe-item.cpp b/src/sp-lpe-item.cpp
index e2f61bfb5..f5c930404 100644
--- a/src/sp-lpe-item.cpp
+++ b/src/sp-lpe-item.cpp
@@ -209,7 +209,7 @@ Inkscape::XML::Node* SPLPEItem::write(Inkscape::XML::Document *xml_doc, Inkscape
/**
* returns true when LPE was successful.
*/
-bool SPLPEItem::performPathEffect(SPCurve *curve, bool is_clip_or_mask) {
+bool SPLPEItem::performPathEffect(SPCurve *curve, SPShape *current, bool is_clip_or_mask) {
if (!curve) {
return false;
@@ -244,9 +244,11 @@ bool SPLPEItem::performPathEffect(SPCurve *curve, bool is_clip_or_mask) {
if (!SP_IS_GROUP(this)) {
lpe->doBeforeEffect_impl(this);
}
-
+ if (SP_IS_GROUP(this) && current) {
+ lpe->setSPCurve(current->getCurve());
+ }
try {
- lpe->doEffect(curve);
+ lpe->doEffect(curve);
}
catch (std::exception & e) {
g_warning("Exception during LPE %s execution. \n %s", lpe->getName().c_str(), e.what());
@@ -695,10 +697,10 @@ SPLPEItem::apply_to_clip_or_mask(SPItem *clip_mask, SPItem *item)
try {
if(SP_IS_GROUP(this)){
c->transform(i2anc_affine(SP_GROUP(item), SP_GROUP(this)));
- success = this->performPathEffect(c, true);
+ success = this->performPathEffect(c, SP_SHAPE(clip_mask), true);
c->transform(i2anc_affine(SP_GROUP(item), SP_GROUP(this)).inverse());
} else {
- success = this->performPathEffect(c, true);
+ success = this->performPathEffect(c, NULL, true);
}
} catch (std::exception & e) {
g_warning("Exception during LPE execution. \n %s", e.what());
@@ -709,12 +711,13 @@ SPLPEItem::apply_to_clip_or_mask(SPItem *clip_mask, SPItem *item)
success = false;
}
Inkscape::XML::Node *repr = clip_mask->getRepr();
- if (success) {
+ // This c check allow to not apply LPE if curve is NULL after performPathEffect used in clone.obgets LPE
+ if (success && c) {
gchar *str = sp_svg_write_path(c->get_pathvector());
repr->setAttribute("d", str);
g_free(str);
} else {
- // LPE was unsuccesfull. Read the old 'd'-attribute.
+ // LPE was unsuccesfull or doeffect stack return null.. Read the old 'd'-attribute.
if (gchar const * value = repr->attribute("d")) {
Geom::PathVector pv = sp_svg_read_pathv(value);
SPCurve *oldcurve = new (std::nothrow) SPCurve(pv);
@@ -724,7 +727,9 @@ SPLPEItem::apply_to_clip_or_mask(SPItem *clip_mask, SPItem *item)
}
}
}
- c->unref();
+ if (c) {
+ c->unref();
+ }
}
}
}
diff --git a/src/sp-lpe-item.h b/src/sp-lpe-item.h
index 9e5cb3329..9cf868cf2 100644
--- a/src/sp-lpe-item.h
+++ b/src/sp-lpe-item.h
@@ -23,6 +23,7 @@
class LivePathEffectObject;
class SPCurve;
+class SPShape;
class SPDesktop;
namespace Inkscape{
@@ -69,7 +70,7 @@ public:
virtual void update_patheffect(bool write);
- bool performPathEffect(SPCurve *curve, bool is_clip_or_mask = false);
+ bool performPathEffect(SPCurve *curve, SPShape *current = NULL, bool is_clip_or_mask = false);
bool pathEffectsEnabled() const;
bool hasPathEffect() const;
diff --git a/src/sp-object.cpp b/src/sp-object.cpp
index e9c60fc7d..c2122e109 100644
--- a/src/sp-object.cpp
+++ b/src/sp-object.cpp
@@ -773,6 +773,20 @@ void SPObject::appendChild(Inkscape::XML::Node *child) {
repr->appendChild(child);
}
+SPObject* SPObject::nthChild(unsigned index) {
+ g_assert(this->repr);
+ if (hasChildren()) {
+ std::vector<SPObject*> l;
+ unsigned counter = 0;
+ for (auto& child: children) {
+ if (counter == index) {
+ return &child;
+ }
+ }
+ }
+ return NULL;
+}
+
void SPObject::addChild(Inkscape::XML::Node *child, Inkscape::XML::Node * prev)
{
g_assert(this->repr);
diff --git a/src/sp-object.h b/src/sp-object.h
index 9abbd324b..d145e966b 100644
--- a/src/sp-object.h
+++ b/src/sp-object.h
@@ -318,6 +318,9 @@ public:
SPObject *lastChild() { return children.empty() ? nullptr : &children.back(); }
SPObject const *lastChild() const { return children.empty() ? nullptr : &children.back(); }
+ SPObject *nthChild(unsigned index);
+ SPObject const *nthChild(unsigned index) const;
+
enum Action { ActionGeneral, ActionBBox, ActionUpdate, ActionShow };
/**
diff --git a/src/sp-path.cpp b/src/sp-path.cpp
index b593b7937..0a2ce4c09 100644
--- a/src/sp-path.cpp
+++ b/src/sp-path.cpp
@@ -354,9 +354,9 @@ g_message("sp_path_update_patheffect writes 'd' attribute");
if (gchar const * value = repr->attribute("d")) {
Geom::PathVector pv = sp_svg_read_pathv(value);
SPCurve *oldcurve = new SPCurve(pv);
-
if (oldcurve) {
- this->setCurve(oldcurve, TRUE);
+ this->setCurveInsync(oldcurve, TRUE);
+ repr->setAttribute("d", value);
oldcurve->unref();
}
}
diff --git a/src/ui/clipboard.cpp b/src/ui/clipboard.cpp
index a8e708597..42fc2fab4 100644
--- a/src/ui/clipboard.cpp
+++ b/src/ui/clipboard.cpp
@@ -410,6 +410,8 @@ const gchar *ClipboardManagerImpl::getFirstObjectID()
strcmp(ch->name(), "svg:use") &&
strcmp(ch->name(), "svg:text") &&
strcmp(ch->name(), "svg:image") &&
+ strcmp(ch->name(), "svg:ellipse") &&
+ strcmp(ch->name(), "svg:circle") &&
strcmp(ch->name(), "svg:rect")
) {
ch = ch->next();