diff --git a/include/parasol/modules/xml.h b/include/parasol/modules/xml.h
index 65847bb69..66729b14b 100644
--- a/include/parasol/modules/xml.h
+++ b/include/parasol/modules/xml.h
@@ -82,7 +82,7 @@ typedef struct XMLAttrib {
std::string Value; // Value of the attribute
inline bool isContent() const { return Name.empty(); }
inline bool isTag() const { return !Name.empty(); }
- XMLAttrib(std::string pName, std::string pValue) : Name(pName), Value(pValue) { };
+ XMLAttrib(std::string pName, std::string pValue = "") : Name(pName), Value(pValue) { };
XMLAttrib() = default;
} XMLATTRIB;
diff --git a/src/svg/animation.cpp b/src/svg/animation.cpp
index 383651572..93020fec9 100644
--- a/src/svg/animation.cpp
+++ b/src/svg/animation.cpp
@@ -3,7 +3,50 @@
// https://www.w3.org/TR/2001/REC-smil-animation-20010904
//********************************************************************************************************************
-// Set common animation properties
+
+void anim_base::activate(extSVG *SVG)
+{
+ // Reset all the variables that control time management and the animation will start from scratch.
+ begin_offset = (double(PreciseTime()) / 1000000.0) - SVG->AnimEpoch;
+ repeat_index = 0;
+ start_time = SVG->AnimEpoch + begin_offset;
+ end_time = 0;
+
+ // Test: w3-animate-elem-21-t.svg
+
+ for (auto &other : start_on_begin) {
+ other->activate(SVG);
+ other->start_time = start_time; // Ensure that times match exactly
+ }
+}
+
+//********************************************************************************************************************
+
+void anim_base::stop(extSVG *SVG, double Time)
+{
+ if (!begin_series.empty()) {
+ // Check if there's a serialised begin offset following the one that's completed.
+ LONG i;
+ for (i=0; i < std::ssize(begin_series)-1; i++) {
+ if (begin_offset IS begin_series[i]) {
+ begin_offset = begin_series[i+1];
+ start_time = 0;
+ return;
+ }
+ }
+ }
+
+ end_time = Time;
+ seek = 1.0; // Necessary in case the seek range calculation has overflowed
+
+ // Start animations that are to be triggered from our ending.
+ for (auto &other : start_on_end) {
+ other->activate(SVG);
+ other->start_time = Time;
+ }
+}
+
+//********************************************************************************************************************
static ERR parse_spline(APTR Path, LONG Index, LONG Command, double X, double Y, anim_base::SPLINE_POINTS &Meta)
{
@@ -17,6 +60,9 @@ static ERR parse_spline(APTR Path, LONG Index, LONG Command, double X, double Y,
return ERR::Okay;
}
+//********************************************************************************************************************
+// Set common animation properties
+
static ERR set_anim_property(extSVG *Self, anim_base &Anim, XMLTag &Tag, ULONG Hash, const std::string_view Value)
{
switch (Hash) {
@@ -735,147 +781,173 @@ void anim_transform::perform(extSVG &SVG)
}
//********************************************************************************************************************
-//
-//
-//
void anim_value::perform(extSVG &SVG)
{
- if ((end_time) and (!freeze)) return;
+ pf::Log log;
+ if ((end_time) and (!freeze)) return;
+
pf::ScopedObjectLock vector(target_vector, 1000);
if (vector.granted()) {
- // Determine the type of the attribute that we're targeting, then interpolate the value and set it.
+ if (vector->Class->ClassID IS ID_VECTORGROUP) {
+ // Groups are a special case because they act as a placeholder and aren't guaranteed to propagate all
+ // attributes to their children.
- switch(StrHash(target_attrib)) {
- case SVF_FONT_SIZE: {
- auto val = get_numeric_value(**vector, FID_FontSize);
- vector->set(FID_FontSize, val);
- break;
- }
+ // Note that group attributes do not override values that are defined by the client.
- case SVF_FILL: {
- auto val = get_colour_value(**vector, FID_FillColour);
- vector->setArray(FID_FillColour, (float *)&val, 4);
- break;
- }
+ for (auto &child : tag->Children) {
+ if (!child.isTag()) continue;
+ // Any tag producing a vector object can theoretically be subject to animation.
+ if (auto si = child.attrib("_id")) {
+ // We can't override attributes that were defined by the client.
+ if (child.attrib(target_attrib)) continue;
- case SVF_FILL_OPACITY: {
- auto val = get_numeric_value(**vector, FID_FillOpacity);
- vector->set(FID_FillOpacity, val);
- break;
+ pf::ScopedObjectLock cv(std::stoi(*si), 1000);
+ if (cv.granted()) set_value(**cv);
+ }
}
+ }
+ else set_value(**vector);
+ }
+}
- case SVF_STROKE: {
- auto val = get_colour_value(**vector, FID_StrokeColour);
- vector->setArray(FID_StrokeColour, (float *)&val, 4);
- break;
- }
+//********************************************************************************************************************
- case SVF_STROKE_WIDTH:
- vector->set(FID_StrokeWidth, get_numeric_value(**vector, FID_StrokeWidth));
- break;
+void anim_value::set_value(objVector &Vector)
+{
+ // Determine the type of the attribute that we're targeting, then interpolate the value and set it.
- case SVF_OPACITY:
- vector->set(FID_Opacity, get_numeric_value(**vector, FID_Opacity));
- break;
+ switch(StrHash(target_attrib)) {
+ case SVF_FONT_SIZE: {
+ auto val = get_numeric_value(Vector, FID_FontSize);
+ Vector.set(FID_FontSize, val);
+ break;
+ }
- case SVF_DISPLAY: {
- auto val = get_string();
- if (StrMatch("none", val) IS ERR::Okay) vector->set(FID_Visibility, LONG(VIS::HIDDEN));
- else if (StrMatch("inline", val) IS ERR::Okay) vector->set(FID_Visibility, LONG(VIS::VISIBLE));
- else if (StrMatch("inherit", val) IS ERR::Okay) vector->set(FID_Visibility, LONG(VIS::INHERIT));
- break;
- }
+ case SVF_FILL: {
+ auto val = get_colour_value(Vector, FID_FillColour);
+ Vector.setArray(FID_FillColour, (float *)&val, 4);
+ break;
+ }
- case SVF_R:
- vector->set(FID_Radius, get_dimension(**vector, FID_Radius));
- break;
+ case SVF_FILL_OPACITY: {
+ auto val = get_numeric_value(Vector, FID_FillOpacity);
+ Vector.set(FID_FillOpacity, val);
+ break;
+ }
- case SVF_RX:
- vector->set(FID_RadiusX, get_dimension(**vector, FID_RadiusX));
- break;
+ case SVF_STROKE: {
+ auto val = get_colour_value(Vector, FID_StrokeColour);
+ Vector.setArray(FID_StrokeColour, (float *)&val, 4);
+ break;
+ }
- case SVF_RY:
- vector->set(FID_RadiusY, get_dimension(**vector, FID_RadiusY));
- break;
+ case SVF_STROKE_WIDTH:
+ Vector.set(FID_StrokeWidth, get_numeric_value(Vector, FID_StrokeWidth));
+ break;
- case SVF_CX:
- vector->set(FID_CX, get_dimension(**vector, FID_CX));
- break;
+ case SVF_OPACITY:
+ Vector.set(FID_Opacity, get_numeric_value(Vector, FID_Opacity));
+ break;
- case SVF_CY:
- vector->set(FID_CY, get_dimension(**vector, FID_CY));
- break;
+ case SVF_DISPLAY: {
+ auto val = get_string();
+ if (StrMatch("none", val) IS ERR::Okay) Vector.set(FID_Visibility, LONG(VIS::HIDDEN));
+ else if (StrMatch("inline", val) IS ERR::Okay) Vector.set(FID_Visibility, LONG(VIS::VISIBLE));
+ else if (StrMatch("inherit", val) IS ERR::Okay) Vector.set(FID_Visibility, LONG(VIS::INHERIT));
+ break;
+ }
+
+ case SVF_VISIBILITY: {
+ auto val = get_string();
+ Vector.set(FID_Visibility, val);
+ break;
+ }
+
+ case SVF_R:
+ Vector.set(FID_Radius, get_dimension(Vector, FID_Radius));
+ break;
+
+ case SVF_RX:
+ Vector.set(FID_RadiusX, get_dimension(Vector, FID_RadiusX));
+ break;
+
+ case SVF_RY:
+ Vector.set(FID_RadiusY, get_dimension(Vector, FID_RadiusY));
+ break;
+
+ case SVF_CX:
+ Vector.set(FID_CX, get_dimension(Vector, FID_CX));
+ break;
+
+ case SVF_CY:
+ Vector.set(FID_CY, get_dimension(Vector, FID_CY));
+ break;
- case SVF_X1:
- vector->set(FID_X1, get_dimension(**vector, FID_X1));
- break;
+ case SVF_X1:
+ Vector.set(FID_X1, get_dimension(Vector, FID_X1));
+ break;
- case SVF_Y1:
- vector->set(FID_Y1, get_dimension(**vector, FID_Y1));
- break;
+ case SVF_Y1:
+ Vector.set(FID_Y1, get_dimension(Vector, FID_Y1));
+ break;
- case SVF_X2:
- vector->set(FID_X2, get_dimension(**vector, FID_X2));
- break;
+ case SVF_X2:
+ Vector.set(FID_X2, get_dimension(Vector, FID_X2));
+ break;
- case SVF_Y2:
- vector->set(FID_Y2, get_dimension(**vector, FID_Y2));
- break;
+ case SVF_Y2:
+ Vector.set(FID_Y2, get_dimension(Vector, FID_Y2));
+ break;
- case SVF_X: {
- if (vector->Class->ClassID IS ID_VECTORGROUP) {
- // Special case: SVG groups don't have an (x,y) position, but can declare one in the form of a
- // transform. Refer to xtag_use() for a working example as to why.
+ case SVF_X: {
+ if (Vector.Class->ClassID IS ID_VECTORGROUP) {
+ // Special case: SVG groups don't have an (x,y) position, but can declare one in the form of a
+ // transform. Refer to xtag_use() for a working example as to why.
- VectorMatrix *m;
- for (m=vector->Matrices; (m) and (m->Tag != MTAG_SVG_TRANSFORM); m=m->Next);
+ VectorMatrix *m;
+ for (m=Vector.Matrices; (m) and (m->Tag != MTAG_SVG_TRANSFORM); m=m->Next);
- if (!m) {
- vecNewMatrix(*vector, &m, false);
- m->Tag = MTAG_SVG_TRANSFORM;
- }
+ if (!m) {
+ vecNewMatrix(&Vector, &m, false);
+ m->Tag = MTAG_SVG_TRANSFORM;
+ }
- if (m) {
- m->TranslateX = get_dimension(**vector, FID_X);
- vecFlushMatrix(m);
- }
+ if (m) {
+ m->TranslateX = get_dimension(Vector, FID_X);
+ vecFlushMatrix(m);
}
- else vector->set(FID_X, get_dimension(**vector, FID_X));
- break;
}
+ else Vector.set(FID_X, get_dimension(Vector, FID_X));
+ break;
+ }
- case SVF_Y: {
- if (vector->Class->ClassID IS ID_VECTORGROUP) {
- VectorMatrix *m;
- for (m=vector->Matrices; (m) and (m->Tag != MTAG_SVG_TRANSFORM); m=m->Next);
+ case SVF_Y: {
+ if (Vector.Class->ClassID IS ID_VECTORGROUP) {
+ VectorMatrix *m;
+ for (m=Vector.Matrices; (m) and (m->Tag != MTAG_SVG_TRANSFORM); m=m->Next);
- if (!m) {
- vecNewMatrix(*vector, &m, false);
- m->Tag = MTAG_SVG_TRANSFORM;
- }
+ if (!m) {
+ vecNewMatrix(&Vector, &m, false);
+ m->Tag = MTAG_SVG_TRANSFORM;
+ }
- if (m) {
- m->TranslateY = get_dimension(**vector, FID_Y);
- vecFlushMatrix(m);
- }
+ if (m) {
+ m->TranslateY = get_dimension(Vector, FID_Y);
+ vecFlushMatrix(m);
}
- else vector->set(FID_Y, get_dimension(**vector, FID_Y));
- break;
}
+ else Vector.set(FID_Y, get_dimension(Vector, FID_Y));
+ break;
+ }
- case SVF_WIDTH:
- vector->set(FID_Width, get_dimension(**vector, FID_Width));
- break;
-
- case SVF_HEIGHT:
- vector->set(FID_Height, get_dimension(**vector, FID_Height));
- break;
+ case SVF_WIDTH:
+ Vector.set(FID_Width, get_dimension(Vector, FID_Width));
+ break;
- case SVF_VISIBILITY:
- vector->set(FID_Visibility, get_string());
- break;
- }
+ case SVF_HEIGHT:
+ Vector.set(FID_Height, get_dimension(Vector, FID_Height));
+ break;
}
-}
+}
\ No newline at end of file
diff --git a/src/svg/animation.h b/src/svg/animation.h
index 43f5dec58..95d6d5704 100644
--- a/src/svg/animation.h
+++ b/src/svg/animation.h
@@ -1,5 +1,6 @@
#include
+#include
static const double DEG2RAD = 0.01745329251994329576923690768489; // Multiple any angle by this value to convert to radians
static const double RAD2DEG = 57.295779513082320876798154814105;
@@ -119,7 +120,6 @@ class anim_base {
double min_duration = 0; // The minimum value of the active duration. If zero, the active duration is not constrained.
double max_duration = 0; // The maximum value of the active duration.
double duration = 0; // Measured in seconds, anything < 0 means infinite.
- double first_time = 0; // Time-stamp of the first iteration
double start_time = 0; // This is time-stamped once the animation has started (the first begin event is hit)
double end_time = 0; // This is time-stamped once the animation has finished all of its cycles (including repetitions)
double end = 0;
@@ -144,40 +144,11 @@ class anim_base {
double get_numeric_value(objVector &, FIELD);
std::string get_string();
FRGB get_colour_value(objVector &, FIELD);
- bool started(double);
+ bool started(extSVG *, double);
bool next_frame(double);
void set_orig_value();
-
- void activate(void) {
- // Reset all the variables that control time management and the animation will start from scratch.
- begin_offset = 0;
- repeat_index = 0;
- start_time = 0;
- end_time = 0;
- }
-
- void stop(double Time) {
- if (!begin_series.empty()) {
- // Check if there's a serialised begin offset following the one that's completed.
- LONG i;
- for (i=0; i < std::ssize(begin_series)-1; i++) {
- if (begin_offset IS begin_series[i]) {
- begin_offset = begin_series[i+1];
- start_time = 0;
- return;
- }
- }
- }
-
- end_time = Time;
- seek = 1.0; // Necessary in case the seek range calculation has overflowed
-
- // Start animations that are to be triggered from our ending.
- for (auto &other : start_on_end) {
- other->activate();
- other->start_time = Time;
- }
- }
+ void activate(extSVG *);
+ void stop(extSVG *, double);
virtual void perform(class extSVG &) = 0;
@@ -245,8 +216,12 @@ class anim_motion : public anim_base {
class anim_value : public anim_base {
public:
- anim_value(OBJECTID pTarget) : anim_base(pTarget) { }
+ XMLTag *tag = NULL;
+
+ anim_value(OBJECTID pTarget, XMLTag *pTag) : anim_base(pTarget), tag(pTag) { }
void perform(extSVG &);
+ void set_value(objVector &Vector);
+
};
//********************************************************************************************************************
diff --git a/src/svg/animation_timing.cpp b/src/svg/animation_timing.cpp
index 1cfc13f6b..a3e8f3b28 100644
--- a/src/svg/animation_timing.cpp
+++ b/src/svg/animation_timing.cpp
@@ -1,27 +1,26 @@
//********************************************************************************************************************
-// Return true if the animation has started
+// Return true if the animation has started. For absolute consistency, animations start 'at the time they should have
+// started', which we can strictly calculate from begin and duration timing values.
-bool anim_base::started(double CurrentTime)
+bool anim_base::started(extSVG *SVG, double CurrentTime)
{
- if (not first_time) first_time = CurrentTime;
-
+ if (end_time) return false;
if (start_time) return true;
if (repeat_index > 0) return true;
- if (begin_offset) {
- // Check if one of the animation's begin triggers has been tripped.
- const double elapsed = CurrentTime - start_time;
- if (elapsed < begin_offset) return false;
- }
+ // Check if the begin trigger has been tripped. If begin_offset is 0 then we start immediately.
+
+ if (CurrentTime < SVG->AnimEpoch + begin_offset) return false;
+ start_time = SVG->AnimEpoch + begin_offset;
+
+ // Start/Reset linked animations that are to start when we do
- // Start/Reset linked animations
for (auto &other : start_on_begin) {
- other->activate();
- other->start_time = CurrentTime;
+ other->activate(SVG);
+ other->start_time = start_time; // Ensure that times match exactly
}
- start_time = CurrentTime;
return true;
}
@@ -71,17 +70,19 @@ static ERR animation_timer(extSVG *SVG, LARGE TimeElapsed, LARGE CurrentTime)
matrix.second.transforms.clear();
}
- for (auto &record : SVG->Animations) {
- std::visit([SVG](auto &&anim) {
- double current_time = double(PreciseTime()) / 1000000.0;
+ if (!SVG->AnimEpoch) {
+ SVG->AnimEpoch = double(CurrentTime) / 1000000.0;
+ }
- if (not anim.started(current_time)) return;
+ double current_time = double(CurrentTime) / 1000000.0;
- if (anim.next_frame(current_time)) {
- anim.perform(*SVG);
- anim.stop(current_time);
- }
- else anim.perform(*SVG);
+ for (auto &record : SVG->Animations) {
+ std::visit([ SVG, current_time ](auto &&anim) {
+ if (not anim.started(SVG, current_time)) return;
+
+ bool stop = anim.next_frame(current_time);
+ anim.perform(*SVG);
+ if (stop) anim.stop(SVG, current_time);
}, record);
}
@@ -140,6 +141,7 @@ static ERR animation_timer(extSVG *SVG, LARGE TimeElapsed, LARGE CurrentTime)
vt.matrix[0] *= t->matrix;
//if (t->additive IS ADD::SUM) vt.matrix[0] *= t->matrix;
//else vt.matrix[0] = t->matrix;
+ vecFlushMatrix(vt.matrix);
});
}
diff --git a/src/svg/class_svg.cpp b/src/svg/class_svg.cpp
index a06a3d5aa..da3825af5 100644
--- a/src/svg/class_svg.cpp
+++ b/src/svg/class_svg.cpp
@@ -76,7 +76,7 @@ static ERR SVG_DataFeed(extSVG *Self, struct acDataFeed *Args)
if (!Args) return ERR::NullArgs;
if (Args->Datatype IS DATA::XML) {
- return load_svg(Self, 0, (CSTRING)Args->Buffer);
+ return parse_svg(Self, 0, (CSTRING)Args->Buffer);
}
return ERR::Okay;
@@ -107,6 +107,7 @@ static ERR SVG_Free(extSVG *Self, APTR Void)
if (Self->Path) { FreeResource(Self->Path); Self->Path = NULL; }
if (Self->Title) { FreeResource(Self->Title); Self->Title = NULL; }
if (Self->Statement) { FreeResource(Self->Statement); Self->Statement = NULL; }
+ if (Self->XML) { FreeResource(Self->XML); Self->XML = NULL; }
return ERR::Okay;
}
@@ -131,8 +132,8 @@ static ERR SVG_Init(extSVG *Self, APTR Void)
else return ERR::NewObject;
}
- if (Self->Path) return load_svg(Self, Self->Path, NULL);
- else if (Self->Statement) return load_svg(Self, NULL, Self->Statement);
+ if (Self->Path) return parse_svg(Self, Self->Path, NULL);
+ else if (Self->Statement) return parse_svg(Self, NULL, Self->Statement);
return ERR::Okay;
}
diff --git a/src/svg/parser.cpp b/src/svg/parser.cpp
index 0b482cd69..dbb702bfe 100644
--- a/src/svg/parser.cpp
+++ b/src/svg/parser.cpp
@@ -124,7 +124,7 @@ static void process_children(extSVG *Self, svgState &State, XMLTag &Tag, OBJECTP
objVector *sibling = NULL;
for (auto &child : Tag.Children) {
if (child.isTag()) {
- xtag_default(Self, State, child, Vector, sibling);
+ xtag_default(Self, State, child, Tag, Vector, sibling);
}
}
}
@@ -140,12 +140,12 @@ static void process_shape_children(extSVG *Self, svgState &State, XMLTag &Tag, O
if (!child.isTag()) continue;
switch(StrHash(child.name())) {
- case SVF_ANIMATE: xtag_animate(Self, child, Vector); break;
- case SVF_ANIMATECOLOR: xtag_animate_colour(Self, child, Vector); break;
+ case SVF_ANIMATE: xtag_animate(Self, child, Tag, Vector); break;
+ case SVF_ANIMATECOLOR: xtag_animate_colour(Self, child, Tag, Vector); break;
case SVF_ANIMATETRANSFORM: xtag_animate_transform(Self, child, Vector); break;
case SVF_ANIMATEMOTION: xtag_animate_motion(Self, child, Vector); break;
+ case SVF_SET: xtag_set(Self, child, Tag, Vector); break;
case SVF_PARASOL_MORPH: xtag_morph(Self, child, Vector); break;
- case SVF_SET: xtag_set(Self, child, Vector); break;
case SVF_TEXTPATH:
if (Vector->Class->ClassID IS ID_VECTORTEXT) {
if (!child.Children.empty()) {
@@ -1236,7 +1236,7 @@ static ERR parse_fe_source(extSVG *Self, svgState &State, objVectorFilter *Filte
// live reference being found.
if (auto tagref = find_href_tag(Self, ref)) {
- xtag_default(Self, State, *tagref, Self->Scene, vector);
+ xtag_default(Self, State, *tagref, Tag, Self->Scene, vector);
}
else log.warning("Element id '%s' not found.", ref.c_str());
}
@@ -1587,6 +1587,7 @@ static ERR process_shape(extSVG *Self, CLASSID VectorID, svgState &State, XMLTag
if (vector->init() IS ERR::Okay) {
process_shape_children(Self, State, Tag, vector);
+ Tag.Attribs.push_back(XMLAttrib { "_id", std::to_string(vector->UID) });
Result = vector;
return error;
}
@@ -1601,7 +1602,7 @@ static ERR process_shape(extSVG *Self, CLASSID VectorID, svgState &State, XMLTag
//********************************************************************************************************************
// See also process_children()
-static ERR xtag_default(extSVG *Self, svgState &State, XMLTag &Tag, OBJECTPTR Parent, objVector * &Vector)
+static ERR xtag_default(extSVG *Self, svgState &State, XMLTag &Tag, XMLTag &ParentTag, OBJECTPTR Parent, objVector * &Vector)
{
pf::Log log(__FUNCTION__);
@@ -1627,11 +1628,11 @@ static ERR xtag_default(extSVG *Self, svgState &State, XMLTag &Tag, OBJECTPTR Pa
case SVF_CONICGRADIENT: xtag_conicgradient(Self, Tag); break;
case SVF_LINEARGRADIENT: xtag_lineargradient(Self, Tag); break;
case SVF_SYMBOL: xtag_symbol(Self, Tag); break;
- case SVF_ANIMATE: xtag_animate(Self, Tag, Parent); break;
- case SVF_ANIMATECOLOR: xtag_animate_colour(Self, Tag, Parent); break;
+ case SVF_ANIMATE: xtag_animate(Self, Tag, ParentTag, Parent); break;
+ case SVF_ANIMATECOLOR: xtag_animate_colour(Self, Tag, ParentTag, Parent); break;
case SVF_ANIMATETRANSFORM: xtag_animate_transform(Self, Tag, Parent); break;
case SVF_ANIMATEMOTION: xtag_animate_motion(Self, Tag, Parent); break;
- case SVF_SET: xtag_set(Self, Tag, Parent); break;
+ case SVF_SET: xtag_set(Self, Tag, ParentTag, Parent); break;
case SVF_FILTER: xtag_filter(Self, State, Tag); break;
case SVF_DEFS: xtag_defs(Self, State, Tag, Parent); break;
case SVF_CLIPPATH: xtag_clippath(Self, Tag); break;
@@ -1920,6 +1921,7 @@ static ERR xtag_image(extSVG *Self, svgState &State, XMLTag &Tag, OBJECTPTR Pare
if (Vector->init() IS ERR::Okay) {
process_shape_children(Self, State, Tag, Vector);
+ Tag.Attribs.push_back(XMLAttrib { "_id", std::to_string(Vector->UID) });
return ERR::Okay;
}
else {
@@ -2161,8 +2163,8 @@ static void xtag_morph(extSVG *Self, XMLTag &Tag, OBJECTPTR Parent)
static void xtag_use(extSVG *Self, svgState &State, XMLTag &Tag, OBJECTPTR Parent)
{
pf::Log log(__FUNCTION__);
- std::string ref;
+ std::string ref;
for (LONG a=1; (a < std::ssize(Tag.Attribs)) and ref.empty(); a++) {
switch(StrHash(Tag.Attribs[a].Name)) {
case SVF_HREF: // SVG2
@@ -2183,11 +2185,6 @@ static void xtag_use(extSVG *Self, svgState &State, XMLTag &Tag, OBJECTPTR Paren
return;
}
- objVector *viewport = NULL;
-
- auto state = State;
- state.applyTag(Tag); // Apply all attribute values to the current state.
-
// Increment the Cloning variable to indicate that we are in a region that is being cloned.
// This is important for some elements like clip-path, whereby the path only needs to be created
// once and can then be referenced multiple times.
@@ -2200,6 +2197,14 @@ static void xtag_use(extSVG *Self, svgState &State, XMLTag &Tag, OBJECTPTR Paren
if ((StrMatch("symbol", tagref->name()) IS ERR::Okay) or (StrMatch("svg", tagref->name()) IS ERR::Okay)) {
// SVG spec requires that we create a VectorGroup and then create a Viewport underneath that. However if there
// are no attributes to apply to the group then there is no sense in creating an empty one.
+
+ // TODO: We should be using the same replace-and-expand tag method that is applied for group
+ // handling, as seen further below in this routine.
+
+ objVector *viewport = NULL;
+
+ auto state = State;
+ state.applyTag(Tag); // Apply all attribute values to the current state.
objVector *group;
bool need_group = false;
@@ -2281,41 +2286,21 @@ static void xtag_use(extSVG *Self, svgState &State, XMLTag &Tag, OBJECTPTR Paren
// additional transformation translate(x,y) is appended to the end (i.e., right-side) of the 'transform'
// attribute on the generated 'g', where x and y represent the values of the 'x' and 'y' attributes on the
// 'use' element. The referenced object and its contents are deep-cloned into the generated tree.
- //
- // NOTE: The SVG documentation appears to be silent on the matter of children in the