• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/svg/parse/svg_node.h"
17 
18 #include "include/core/SkClipOp.h"
19 
20 #include "core/components/common/painter/rosen_svg_painter.h"
21 #include "core/components/common/properties/decoration.h"
22 #include "core/components_ng/render/drawing.h"
23 #include "core/components_ng/svg/base/svg_length_scale_rule.h"
24 #include "core/components_ng/svg/parse/svg_animation.h"
25 #include "core/components_ng/svg/parse/svg_attributes_parser.h"
26 #include "core/components_ng/svg/parse/svg_constants.h"
27 #include "core/components_ng/svg/parse/svg_filter.h"
28 #include "core/components_ng/svg/parse/svg_mask.h"
29 #include "core/components_ng/svg/parse/svg_clip_path.h"
30 #include "core/components_ng/svg/parse/svg_gradient.h"
31 #include "core/components_ng/svg/parse/svg_transform.h"
32 #include "core/pipeline_ng/pipeline_context.h"
33 
34 namespace OHOS::Ace::NG {
35 namespace {
36 
37 constexpr size_t SVG_ATTR_ID_FLAG_NUMS = 6;
38 const char VALUE_NONE[] = "none";
39 const char ATTR_NAME_OPACITY[] = "opacity";
40 const char DOM_SVG_SRC_FILL_OPACITY[] = "fill-opacity";
41 const char DOM_SVG_SRC_FILL_RULE[] = "fill-rule";
42 const char DOM_SVG_SRC_STROKE_DASHARRAY[] = "stroke-dasharray";
43 const char DOM_SVG_SRC_STROKE_DASHOFFSET[] = "stroke-dashoffset";
44 const char DOM_SVG_SRC_STROKE_LINECAP[] = "stroke-linecap";
45 const char DOM_SVG_SRC_STROKE_LINEJOIN[] = "stroke-linejoin";
46 const char DOM_SVG_SRC_STROKE_MITERLIMIT[] = "stroke-miterlimit";
47 const char DOM_SVG_SRC_STROKE_OPACITY[] = "stroke-opacity";
48 const char DOM_SVG_SRC_STROKE_WIDTH[] = "stroke-width";
49 const char DOM_SVG_SRC_CLIP_PATH[] = "clip-path";
50 const char DOM_SVG_SRC_CLIP_RULE[] = "clip-rule";
51 const char DOM_SVG_SRC_TRANSFORM_ORIGIN[] = "transform-origin";
52 
ParseIdFromUrl(const std::string & url)53 std::string ParseIdFromUrl(const std::string& url)
54 {
55     if (url.size() > SVG_ATTR_ID_FLAG_NUMS) {
56         std::string::size_type start = url.find("url(#");
57         if (start != std::string::npos) {
58             start += std::strlen("url(#");
59             std::string::size_type end = url.find_first_of(')', start);
60             if (end != std::string::npos) {
61                 return url.substr(start, end - start);
62             }
63         }
64     }
65     return "";
66 }
67 
68 const std::unordered_map<std::string, std::function<Color(SvgBaseAttribute&)>> COLOR_GETTERS = {
69     { ATTR_NAME_FILL,
__anon712ba1890202() 70         [](SvgBaseAttribute& attr) -> Color { return attr.fillState.GetColor(); } },
71     { ATTR_NAME_STROKE,
__anon712ba1890302() 72         [](SvgBaseAttribute& attr) -> Color { return attr.strokeState.GetColor(); } },
73 };
74 
75 const std::unordered_map<std::string, std::function<Dimension(SvgBaseAttribute&)>> DIMENSION_GETTERS = {
76     { ATTR_NAME_STROKE_WIDTH,
__anon712ba1890402() 77         [](SvgBaseAttribute& attr) -> Dimension {
78             return attr.strokeState.GetLineWidth();
79         } },
80 };
81 
82 const std::unordered_map<std::string, std::function<double(SvgBaseAttribute&)>> DOUBLE_GETTERS = {
83     { ATTR_NAME_FILL_OPACITY,
__anon712ba1890502() 84         [](SvgBaseAttribute& attr) -> double {
85             return attr.fillState.GetOpacity().GetValue();
86         } },
87     { ATTR_NAME_STROKE_OPACITY,
__anon712ba1890602() 88         [](SvgBaseAttribute& attr) -> double {
89             return attr.strokeState.GetOpacity().GetValue();
90         } },
91     { ATTR_NAME_MITER_LIMIT,
__anon712ba1890702() 92         [](SvgBaseAttribute& attr) -> double {
93             return attr.strokeState.GetMiterLimit();
94         } },
95     { ATTR_NAME_STROKE_DASH_OFFSET,
__anon712ba1890802() 96         [](SvgBaseAttribute& attr) -> double {
97             return attr.strokeState.GetLineDash().dashOffset;
98         } },
99     { ATTR_NAME_OPACITY,
__anon712ba1890902() 100         [](SvgBaseAttribute& attr) -> double {
101             return attr.opacity * (1.0 / UINT8_MAX);
102         } },
103 };
104 
GetNodeIdFromUrl(const std::string & url)105 std::string GetNodeIdFromUrl(const std::string& url)
106 {
107     return std::regex_replace(url, std::regex(R"(^url\(\s*['"]?\s*#([^()]+?)\s*['"]?\s*\)$)"), "$1");
108 }
109 
110 void SetCompatibleFill(const std::string& value, SvgBaseAttribute& attrs)
111 {
112     if (value.find("url(") == 0) {
113         attrs.fillState.SetHref(GetNodeIdFromUrl(value));
114         return;
115     }
116     attrs.fillState.SetColor((value == VALUE_NONE ? Color::TRANSPARENT : SvgAttributesParser::GetColor(value)));
117 }
118 
119 void SetCompatibleStroke(const std::string& value, SvgBaseAttribute& attrs)
120 {
121     if (value.find("url(") == 0) {
122         attrs.strokeState.SetHref(GetNodeIdFromUrl(value));
123         return;
124     }
125     attrs.strokeState.SetColor((value == VALUE_NONE ? Color::TRANSPARENT : SvgAttributesParser::GetColor(value)));
126 }
127 } // namespace
128 
129 uint8_t OpacityDoubleToUint8(double opacity)
130 {
131     return static_cast<uint8_t>(std::round(opacity * UINT8_MAX));
132 }
133 
134 void SvgNode::SetAttr(const std::string& name, const std::string& value)
135 {
136     if (ParseAndSetSpecializedAttr(name, value)) {
137         return;
138     }
139     static const LinearMapNode<void (*)(const std::string&, SvgBaseAttribute&)> SVG_BASE_ATTRS[] = {
140         { DOM_SVG_SRC_CLIP_PATH,
141             [](const std::string& val, SvgBaseAttribute& attrs) {
142                 auto value = StringUtils::TrimStr(val);
143                 if (value.find("url(") == 0) {
144                     auto src = std::regex_replace(value,
145                         std::regex(R"(^url\(\s*['"]?\s*#([^()]+?)\s*['"]?\s*\)$)"), "$1");
146                     attrs.clipState.SetHref(src);
147                 }
148             } },
149         { DOM_SVG_SRC_CLIP_RULE,
150             [](const std::string& val, SvgBaseAttribute& attrs) {
151                 attrs.clipState.SetClipRule(val == "evenodd" ? SvgRuleType::SVG_RULE_EVENODD :
152                     SvgRuleType::SVG_RULE_NONEZERO);
153             } },
154         { DOM_CLIP_PATH,
155             [](const std::string& val, SvgBaseAttribute& attrs) {
156                 auto value = StringUtils::TrimStr(val);
157                 if (value.find("url(") == 0) {
158                     auto src = std::regex_replace(value,
159                         std::regex(R"(^url\(\s*['"]?\s*#([^()]+?)\s*['"]?\s*\)$)"), "$1");
160                     attrs.clipState.SetHref(src);
161                 }
162             } },
163         { SVG_CLIP_PATH_UNITS,
__anon712ba1890d02() 164             [](const std::string& val, SvgBaseAttribute& attrs) {
165                 attrs.clipState.SetClipPathUnits((val == "objectBoundingBox") ?
166                     SvgLengthScaleUnit::OBJECT_BOUNDING_BOX : SvgLengthScaleUnit::USER_SPACE_ON_USE);
167             } },
168         { SVG_CLIP_RULE,
__anon712ba1890e02() 169             [](const std::string& val, SvgBaseAttribute& attrs) {
170                 attrs.clipState.SetClipRule(val == "evenodd" ? SvgRuleType::SVG_RULE_EVENODD :
171                     SvgRuleType::SVG_RULE_NONEZERO);
172             } },
173         { SVG_FILL,
__anon712ba1890f02() 174             [](const std::string& val, SvgBaseAttribute& attrs) {
175                 auto value = StringUtils::TrimStr(val);
176                 if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_FOURTEEN)) {
177                     SetCompatibleFill(value, attrs);
178                     return;
179                 }
180                 Color color;
181                 if (val == VALUE_NONE || SvgAttributesParser::ParseColor(value, color)) {
182                     attrs.fillState.SetColor((val == VALUE_NONE ? Color::TRANSPARENT : color));
183                     attrs.fillState.SetIsFillNone(val == VALUE_NONE);
184                     return;
185                 }
186                 if (value.find("url(") == 0) {
187                     auto src = std::regex_replace(value,
188                         std::regex(R"(^url\(\s*['"]?\s*#([^()]+?)\s*['"]?\s*\)$)"), "$1");
189                     attrs.fillState.SetHref(src);
190                 }
191             } },
192         { DOM_SVG_SRC_FILL_OPACITY,
193             [](const std::string& val, SvgBaseAttribute& attrs) {
194                 attrs.fillState.SetOpacity(std::clamp(StringUtils::StringToDouble(val), 0.0, 1.0));
195             } },
196         { DOM_SVG_SRC_FILL_RULE,
197             [](const std::string& val, SvgBaseAttribute& attrs) {
198                 attrs.fillState.SetFillRule(val);
199             } },
200         { SVG_FILL_OPACITY,
201             [](const std::string& val, SvgBaseAttribute& attrs) {
202                 attrs.fillState.SetOpacity(std::clamp(StringUtils::StringToDouble(val), 0.0, 1.0));
203             } },
204         { SVG_FILL_RULE,
205             [](const std::string& val, SvgBaseAttribute& attrs) {
206                 attrs.fillState.SetFillRule(val);
207             } },
208         { SVG_FILTER,
209             [](const std::string& val, SvgBaseAttribute& attrs) {
210                 attrs.filterId = val;
211             } },
212         { SVG_FONT_SIZE,
213             [](const std::string& val, SvgBaseAttribute& attrs) {
214                 Dimension fontSize = StringUtils::StringToDimension(val);
215                 if (GreatOrEqual(fontSize.Value(), 0.0)) {
216                     attrs.textStyle.SetFontSize(fontSize);
217                 }
218             } },
219         { SVG_HREF,
220             [](const std::string& val, SvgBaseAttribute& attrs) {
221                 auto value = StringUtils::TrimStr(val);
222                 if (value.substr(0, 1) == "#") {
223                     attrs.href = value.substr(1);
224                 }
225             } },
226         { SVG_MASK,
227             [](const std::string& val, SvgBaseAttribute& attrs) {
228                 attrs.maskId = val;
229             } },
230         { SVG_OPACITY,
231             [](const std::string& val, SvgBaseAttribute& attrs) {
232                 attrs.hasOpacity = true;
233                 attrs.opacity = std::clamp(StringUtils::StringToDouble(val), 0.0, 1.0);
234             } },
235         { SVG_PATTERN_TRANSFORM,
236             [](const std::string& val, SvgBaseAttribute& attrs) {
237                 attrs.transform = val;
238             } },
239         { SVG_STROKE,
240             [](const std::string& val, SvgBaseAttribute& attrs) {
241                 auto value = StringUtils::TrimStr(val);
242                 if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_FOURTEEN)) {
243                     SetCompatibleStroke(value, attrs);
244                     return;
245                 }
246                 Color color;
247                 if (val == VALUE_NONE || SvgAttributesParser::ParseColor(value, color)) {
248                     attrs.strokeState.SetColor((val == VALUE_NONE ? Color::TRANSPARENT : color));
249                     return;
250                 }
251                 if (value.find("url(") == 0) {
252                     auto src = std::regex_replace(value,
253                         std::regex(R"(^url\(\s*['"]?\s*#([^()]+?)\s*['"]?\s*\)$)"), "$1");
254                     attrs.strokeState.SetHref(src);
255                 }
256             } },
257         { DOM_SVG_SRC_STROKE_DASHARRAY,
258             [](const std::string& val, SvgBaseAttribute& attrs) {
259                 if (val.empty()) {
260                     return;
261                 }
262                 std::vector<double> lineDashVector;
263                 std::string handledStr = StringUtils::ReplaceChar(val, ',', ' ');
264                 StringUtils::StringSplitter(handledStr, ' ', lineDashVector);
265                 attrs.strokeState.SetLineDash(lineDashVector);
266             } },
267         { DOM_SVG_SRC_STROKE_DASHOFFSET,
268             [](const std::string& val, SvgBaseAttribute& attrs) {
269                 attrs.strokeState.SetLineDashOffset(StringUtils::StringToDouble(val));
270             } },
271         { DOM_SVG_SRC_STROKE_LINECAP,
272             [](const std::string& val, SvgBaseAttribute& attrs) {
273                 attrs.strokeState.SetLineCap(SvgAttributesParser::GetLineCapStyle(val));
274             } },
275         { DOM_SVG_SRC_STROKE_LINEJOIN,
276             [](const std::string& val, SvgBaseAttribute& attrs) {
277                 attrs.strokeState.SetLineJoin(SvgAttributesParser::GetLineJoinStyle(val));
278             } },
279         { DOM_SVG_SRC_STROKE_MITERLIMIT,
280             [](const std::string& val, SvgBaseAttribute& attrs) {
281                 double miterLimit = StringUtils::StringToDouble(val);
282                 if (GreatOrEqual(miterLimit, 1.0)) {
283                     attrs.strokeState.SetMiterLimit(miterLimit);
284                 }
285             } },
286         { DOM_SVG_SRC_STROKE_OPACITY,
287             [](const std::string& val, SvgBaseAttribute& attrs) {
288                 attrs.strokeState.SetOpacity(std::clamp(StringUtils::StringToDouble(val), 0.0, 1.0));
289             } },
290         { DOM_SVG_SRC_STROKE_WIDTH,
291             [](const std::string& val, SvgBaseAttribute& attrs) {
292                 Dimension lineWidth = StringUtils::StringToDimension(val);
293                 if (GreatOrEqual(lineWidth.Value(), 0.0)) {
294                     attrs.strokeState.SetLineWidth(lineWidth);
295                 }
296             } },
297         { SVG_STROKE_DASHARRAY,
298             [](const std::string& val, SvgBaseAttribute& attrs) {
299                 if (val.empty()) {
300                     return;
301                 }
302                 std::vector<double> lineDashVector;
303                 StringUtils::StringSplitter(val, ' ', lineDashVector);
304                 attrs.strokeState.SetLineDash(lineDashVector);
305             } },
306         { SVG_STROKE_DASHOFFSET,
307             [](const std::string& val, SvgBaseAttribute& attrs) {
308                 attrs.strokeState.SetLineDashOffset(StringUtils::StringToDouble(val));
309             } },
310         { SVG_STROKE_LINECAP,
311             [](const std::string& val, SvgBaseAttribute& attrs) {
312                 attrs.strokeState.SetLineCap(SvgAttributesParser::GetLineCapStyle(val));
313             } },
314         { SVG_STROKE_LINEJOIN,
315             [](const std::string& val, SvgBaseAttribute& attrs) {
316                 attrs.strokeState.SetLineJoin(SvgAttributesParser::GetLineJoinStyle(val));
317             } },
318         { SVG_STROKE_MITERLIMIT,
319             [](const std::string& val, SvgBaseAttribute& attrs) {
320                 double miterLimit = StringUtils::StringToDouble(val);
321                 if (GreatOrEqual(miterLimit, 1.0)) {
322                     attrs.strokeState.SetMiterLimit(miterLimit);
323                 }
324             } },
325         { SVG_STROKE_OPACITY,
326             [](const std::string& val, SvgBaseAttribute& attrs) {
327                 attrs.strokeState.SetOpacity(std::clamp(StringUtils::StringToDouble(val), 0.0, 1.0));
328             } },
329         { SVG_STROKE_WIDTH,
330             [](const std::string& val, SvgBaseAttribute& attrs) {
331                 Dimension lineWidth = StringUtils::StringToDimension(val);
332                 if (GreatOrEqual(lineWidth.Value(), 0.0)) {
333                     attrs.strokeState.SetLineWidth(lineWidth);
334                 }
335             } },
336         { SVG_TRANSFORM,
337             [](const std::string& val, SvgBaseAttribute& attrs) {
338                 attrs.transform = val;
339                 attrs.transformVec = SvgAttributesParser::GetTransformInfo(val);
340                 if (attrs.transformVec.empty()) {
341                     TAG_LOGW(AceLogTag::ACE_IMAGE, "Set transformVec failed. transform:[%{public}s]", val.c_str());
342                 }
343             } },
344         { DOM_SVG_SRC_TRANSFORM_ORIGIN,
345             [](const std::string& val, SvgBaseAttribute& attrs) {
346                 attrs.transformOrigin = SvgAttributesParser::GetTransformOrigin(val);
347             } },
348         { SVG_XLINK_HREF,
349             [](const std::string& val, SvgBaseAttribute& attrs) {
350                 auto value = StringUtils::TrimStr(val);
351                 if (value.substr(0, 1) == "#") {
352                     attrs.href = value.substr(1);
353                 }
354             } }
355     };
356     auto attrIter = BinarySearchFindIndex(SVG_BASE_ATTRS, ArraySize(SVG_BASE_ATTRS), name.c_str());
357     if (attrIter != -1) {
358         SVG_BASE_ATTRS[attrIter].value(value, attributes_);
359     }
360 }
361 
362 RSPath SvgNode::AsRSPath(const Size& viewPort) const
363 {
364     return {};
365 }
366 
367 void SvgNode::ProcessSvgStyle(RefPtr<SvgNode> svgNode, const SvgBaseAttribute& attr)
368 {
369     svgNode->InheritAttr(attr);
370     auto& svgAttributes = svgNode->attributes_;
371     if (svgNode->hrefFill_) {
372         auto href = svgAttributes.fillState.GetHref();
373         if (!href.empty()) {
374             auto gradient = svgNode->GetGradient(href);
375             if (gradient) {
376                 svgAttributes.fillState.SetGradient(gradient.value());
377             }
378         }
379         href = svgAttributes.strokeState.GetHref();
380         if (!href.empty()) {
381             auto gradient = svgNode->GetGradient(href);
382             if (gradient) {
383                 svgAttributes.strokeState.SetGradient(gradient.value());
384             }
385         }
386     }
387     if (svgNode->hrefRender_) {
388         svgNode->hrefClipPath_ = svgAttributes.clipState.GetHref();
389         svgNode->opacity_ = OpacityDoubleToUint8(svgAttributes.opacity);
390         svgNode->transform_ = svgAttributes.transform;
391         svgNode->hrefMaskId_ = ParseIdFromUrl(svgAttributes.maskId);
392         svgNode->hrefFilterId_ = ParseIdFromUrl(svgAttributes.filterId);
393     }
394     svgNode->OnInitStyle();
395 }
396 
397 void SvgNode::ProcessChildAnimations(const RefPtr<SvgNode>& currentSvgNode)
398 {
399     for (auto& child : currentSvgNode->children_) {
400         auto svgAnimate = DynamicCast<SvgAnimation>(child);
401         if (svgAnimate) {
402             svgAnimate->UpdateAttr();
403             currentSvgNode->PrepareAnimation(svgAnimate);
404         }
405     }
406 }
407 
408 bool SvgNode::ProcessChildStyle(SvgInitStyleProcessInfo& currentSvgNodeInfo,
409     std::stack<std::pair<SvgInitStyleProcessInfo, const SvgBaseAttribute*>>& initStyleTaskSt)
410 {
411     auto currentSvgNode = currentSvgNodeInfo.svgNode;
412     if (currentSvgNode->passStyle_ &&
413         currentSvgNodeInfo.childIndex < static_cast<int32_t>(currentSvgNode->children_.size())) {
414         auto child = currentSvgNode->children_[currentSvgNodeInfo.childIndex];
415         if (child) {
416             // pass down style only if child inheritStyle_ is true
417             initStyleTaskSt.emplace(
418                 child, (child->inheritStyle_) ? &(currentSvgNode->attributes_) : nullptr);
419         }
420         ++currentSvgNodeInfo.childIndex;
421         return false;
422     }
423     return true;
424 }
425 
426 void SvgNode::InitStyleDfs(const WeakPtr<SvgNode>& root, const SvgBaseAttribute& attr)
427 {
428     auto parentNode = root.Upgrade();
429     if (!parentNode) {
430         return;
431     }
432     std::stack<std::pair<SvgInitStyleProcessInfo, const SvgBaseAttribute*>> initStyleTaskSt;
433     initStyleTaskSt.emplace(parentNode, &attr);
434     while (!initStyleTaskSt.empty()) {
435         auto& [currentSvgNodeInfo, currentAttr] = initStyleTaskSt.top();
436         if (currentSvgNodeInfo.childIndex == 0) {
437             currentSvgNodeInfo.svgNode->ProcessSvgStyle(
438                 currentSvgNodeInfo.svgNode, currentAttr ? *currentAttr : SvgBaseAttribute());
439         }
440         if (currentSvgNodeInfo.svgNode->ProcessChildStyle(currentSvgNodeInfo, initStyleTaskSt)) {
441             currentSvgNodeInfo.svgNode->ProcessChildAnimations(currentSvgNodeInfo.svgNode);
442             initStyleTaskSt.pop();
443         }
444     }
445 }
446 
447 void SvgNode::InitStyle(const SvgBaseAttribute& attr)
448 {
449     InitStyleDfs(WeakClaim(this), attr);
450 }
451 
452 void SvgNode::Draw(RSCanvas& canvas, const Size& viewPort, const std::optional<Color>& color)
453 {
454     if (isDrawing_) {
455         TAG_LOGW(AceLogTag::ACE_IMAGE,
456             "The current node is already in the process of being drawn in the SVG rendering flow.");
457         return;
458     }
459     if (!OnCanvas(canvas)) {
460         TAG_LOGW(AceLogTag::ACE_IMAGE, "Svg Draw failed(Reason: Canvas is null).");
461         return;
462     }
463     isDrawing_ = true;
464     // mask and filter create extra layers, need to record initial layer count
465     auto count = rsCanvas_->GetSaveCount();
466     rsCanvas_->Save();
467     if (!hrefClipPath_.empty()) {
468         OnClipPath(canvas, viewPort);
469     } else if (isRootNode_) {
470         AdjustContentAreaByViewBox(canvas, viewPort);
471     }
472     if (!transform_.empty() || !animateTransform_.empty()) {
473         OnTransform(canvas, viewPort);
474     }
475     if (!hrefMaskId_.empty()) {
476         OnMask(canvas, viewPort);
477     }
478     if (!hrefFilterId_.empty()) {
479         OnFilter(canvas, viewPort);
480     }
481 
482     OnDraw(canvas, viewPort, color);
483     OnDrawTraversed(canvas, viewPort, color);
484     rsCanvas_->RestoreToCount(count);
485     isDrawing_ = false; // end the drawing process.
486 }
487 
488 void SvgNode::Draw(RSCanvas& canvas, const SvgLengthScaleRule& lengthRule)
489 {
490     // mask and filter create extra layers, need to record initial layer count
491     auto count = canvas.GetSaveCount();
492     if (isDrawing_) {
493         TAG_LOGW(AceLogTag::ACE_IMAGE,
494             "The current node is already in the process of being drawn in the SVG rendering flow.");
495         return;
496     }
497     canvas.Save();
498     isDrawing_ = true;
499     auto rsBounds = AsPath(lengthRule).GetBounds();
500     Rect containerRect(rsBounds.GetLeft(), rsBounds.GetTop(), rsBounds.GetWidth(), rsBounds.GetHeight());
501     // rect use rsBounds, other use lengthRule containerRect
502     if (LessOrEqual(rsBounds.GetWidth(), 0.0f) || LessOrEqual(rsBounds.GetHeight(), 0.0f)) {
503         containerRect = lengthRule.GetContainerRect();
504     }
505     SvgCoordinateSystemContext svgCoordinateSystemContext(containerRect, lengthRule.GetViewPort());
506     TAG_LOGD(AceLogTag::ACE_IMAGE, "l:%{public}lf, t:%{public}lf, r:%{public}lf, b:%{public}lf, units:%{public}d",
507         rsBounds.GetLeft(), rsBounds.GetTop(), rsBounds.GetRight(), rsBounds.GetBottom(),
508         (int)lengthRule.GetLengthScaleUnit());
509     if (!hrefClipPath_.empty()) {
510         OnClipPath(canvas, svgCoordinateSystemContext);
511     } else if (isRootNode_) {
512         auto svgContext = svgContext_.Upgrade();
513         if (svgContext) {
514             AdjustContentAreaByViewBox(canvas, svgContext->GetViewPort());
515         }
516     }
517     if (!transform_.empty() || !animateTransform_.empty()) {
518         OnTransform(canvas, lengthRule);
519     }
520     if (!hrefMaskId_.empty()) {
521         OnMask(canvas, svgCoordinateSystemContext);
522     }
523     if (!hrefFilterId_.empty()) {
524         OnFilter(canvas, svgCoordinateSystemContext);
525     }
526 
527     OnDraw(canvas, lengthRule);
528     OnDrawTraversed(canvas, lengthRule);
529     canvas.RestoreToCount(count);
530     isDrawing_ = false;
531 }
532 
533 void SvgNode::OnDrawTraversed(RSCanvas& canvas, const Size& viewPort, const std::optional<Color>& color)
534 {
535     auto smoothEdge = GetSmoothEdge();
536     auto colorFilter = GetColorFilter();
537     for (auto& node : children_) {
538         if (node && node->drawTraversed_) {
539             if (GreatNotEqual(smoothEdge, 0.0f)) {
540                 node->SetSmoothEdge(smoothEdge);
541             }
542             node->SetColorFilter(colorFilter);
543             node->Draw(canvas, viewPort, color);
544         }
545     }
546 }
547 
548 void SvgNode::OnDrawTraversed(RSCanvas& canvas, const SvgLengthScaleRule& lengthRule)
549 {
550     auto smoothEdge = GetSmoothEdge();
551     auto colorFilter = GetColorFilter();
552     for (auto& node : children_) {
553         if (node && node->drawTraversed_) {
554             if (GreatNotEqual(smoothEdge, 0.0f)) {
555                 node->SetSmoothEdge(smoothEdge);
556             }
557             node->SetColorFilter(colorFilter);
558             node->Draw(canvas, lengthRule);
559         }
560     }
561 }
562 
563 bool SvgNode::OnCanvas(RSCanvas& canvas)
564 {
565     rsCanvas_ = &canvas;
566     return true;
567 }
568 
569 void SvgNode::OnClipPath(RSCanvas& canvas, const Size& viewPort)
570 {
571     auto svgContext = svgContext_.Upgrade();
572     CHECK_NULL_VOID(svgContext);
573     auto refSvgNode = svgContext->GetSvgNodeById(hrefClipPath_);
574     CHECK_NULL_VOID(refSvgNode);
575     auto clipPath = refSvgNode->AsPath(viewPort);
576     if (!clipPath.IsValid()) {
577         LOGW("OnClipPath abandon, clipPath is empty");
578         return;
579     }
580     rsCanvas_->ClipPath(clipPath, RSClipOp::INTERSECT, true);
581 }
582 
583 void SvgNode::OnClipPath(RSCanvas& canvas, const SvgCoordinateSystemContext& svgCoordinateSystemContext)
584 {
585     auto svgContext = svgContext_.Upgrade();
586     CHECK_NULL_VOID(svgContext);
587     auto refSvgNode = svgContext->GetSvgNodeById(hrefClipPath_);
588     CHECK_NULL_VOID(refSvgNode);
589     if (!AceType::InstanceOf<SvgClipPath>(refSvgNode)) {
590         return;
591     }
592     refSvgNode->OnClipEffect(canvas, svgCoordinateSystemContext);
593 }
594 
595 void SvgNode::OnFilter(RSCanvas& canvas, const Size& viewPort)
596 {
597     if (!AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
598         return;
599     }
600     auto svgContext = svgContext_.Upgrade();
601     CHECK_NULL_VOID(svgContext);
602     auto refFilter = svgContext->GetSvgNodeById(hrefFilterId_);
603     CHECK_NULL_VOID(refFilter);
604     auto effectPath = AsPath(viewPort);
605     auto bounds = effectPath.GetBounds();
606     refFilter->SetEffectFilterArea({
607         useOffsetX_ + bounds.GetLeft(), useOffsetY_ + bounds.GetTop(),
608         bounds.GetWidth(), bounds.GetHeight()
609     });
610     refFilter->Draw(canvas, viewPort, std::nullopt);
611     return;
612 }
613 
614 void SvgNode::OnFilter(RSCanvas& canvas, const SvgCoordinateSystemContext& svgCoordinateSystemContext)
615 {
616     auto svgContext = svgContext_.Upgrade();
617     CHECK_NULL_VOID(svgContext);
618     auto refFilter = svgContext->GetSvgNodeById(hrefFilterId_);
619     CHECK_NULL_VOID(refFilter);
620     if (!AceType::InstanceOf<SvgFilter>(refFilter)) {
621         return;
622     }
623     refFilter->OnFilterEffect(canvas, svgCoordinateSystemContext, useOffsetX_, useOffsetY_);
624 }
625 
626 void SvgNode::OnMask(RSCanvas& canvas, const Size& viewPort)
627 {
628     auto svgContext = svgContext_.Upgrade();
629     CHECK_NULL_VOID(svgContext);
630     auto refMask = svgContext->GetSvgNodeById(hrefMaskId_);
631     CHECK_NULL_VOID(refMask);
632     refMask->Draw(canvas, viewPort, std::nullopt);
633     return;
634 }
635 
636 void SvgNode::OnMask(RSCanvas& canvas, const SvgCoordinateSystemContext& svgCoordinateSystemContext)
637 {
638     auto svgContext = svgContext_.Upgrade();
639     CHECK_NULL_VOID(svgContext);
640     auto refMask = svgContext->GetSvgNodeById(hrefMaskId_);
641     CHECK_NULL_VOID(refMask);
642     if (!AceType::InstanceOf<SvgMask>(refMask)) {
643         return;
644     }
645     refMask->OnMaskEffect(canvas, svgCoordinateSystemContext);
646 }
647 
648 void SvgNode::OnTransform(RSCanvas& canvas, const Size& viewPort)
649 {
650     auto matrix = (animateTransform_.empty()) ? SvgTransform::CreateMatrix4(transform_)
651                                               : SvgTransform::CreateMatrixFromMap(animateTransform_);
652     canvas.ConcatMatrix(RosenSvgPainter::ToDrawingMatrix(matrix));
653 }
654 
655 void SvgNode::OnTransform(RSCanvas& canvas, const SvgLengthScaleRule& lengthRule)
656 {
657     Offset globalPivot = CalcGlobalPivot(attributes_.transformOrigin, lengthRule);
658     auto matrix = (animateTransform_.empty()) ?
659         NGSvgTransform::CreateMatrix4(attributes_.transformVec, globalPivot, lengthRule)
660         : SvgTransform::CreateMatrixFromMap(animateTransform_);
661     canvas.ConcatMatrix(RosenSvgPainter::ToDrawingMatrix(matrix));
662 }
663 
664 float SvgNode::GetRegionLength(Dimension origin, const SvgLengthScaleRule& rule, SvgLengthType lengthType)
665 {
666     float length = 0.0f;
667     switch (lengthType) {
668         case SvgLengthType::HORIZONTAL:
669             length = (rule.GetLengthScaleUnit() == SvgLengthScaleUnit::USER_SPACE_ON_USE) ?
670                      ConvertDimensionToPx(origin, rule.GetViewPort().Width()) :
671                      origin.Value() * rule.GetContainerRect().Width();
672             break;
673         case SvgLengthType::VERTICAL:
674             length = (rule.GetLengthScaleUnit() == SvgLengthScaleUnit::USER_SPACE_ON_USE) ?
675                      ConvertDimensionToPx(origin, rule.GetViewPort().Height()) :
676                      origin.Value() * rule.GetContainerRect().Height();
677             break;
678         case SvgLengthType::OTHER:
679             auto width = rule.GetContainerRect().Width();
680             auto height = rule.GetContainerRect().Height();
681             auto baseLength = std::sqrt(width * width + height * height) / std::sqrt(2.0f);
682             length = rule.GetLengthScaleUnit() == SvgLengthScaleUnit::USER_SPACE_ON_USE ?
683                 ConvertDimensionToPx(origin, baseLength) : origin.Value() * baseLength;
684             break;
685     }
686     return length;
687 }
688 
689 float SvgNode::GetRegionPosition(Dimension origin, const SvgLengthScaleRule& rule,
690     SvgLengthType lengthType)
691 {
692     auto position = 0.0f;
693     switch (lengthType) {
694         case SvgLengthType::HORIZONTAL:
695             position = (rule.GetLengthScaleUnit() == SvgLengthScaleUnit::USER_SPACE_ON_USE) ?
696                      ConvertDimensionToPx(origin, rule.GetViewPort().Width()) :
697                      origin.Value() * rule.GetContainerRect().Width() + rule.GetContainerRect().Left();
698             break;
699         case SvgLengthType::VERTICAL:
700             position = (rule.GetLengthScaleUnit() == SvgLengthScaleUnit::USER_SPACE_ON_USE) ?
701                      ConvertDimensionToPx(origin, rule.GetViewPort().Height()) :
702                      origin.Value() * rule.GetContainerRect().Height() + rule.GetContainerRect().Top();
703             break;
704         default:
705             break;
706     }
707     return position;
708 }
709 
710 float SvgNode::GetMeasuredLength(Dimension origin, const SvgLengthScaleRule& rule,
711     SvgLengthType lengthType)
712 {
713     float ContainerLength = 0.0f;
714     switch (lengthType) {
715         case SvgLengthType::HORIZONTAL:
716             ContainerLength = rule.GetContainerRect().Width();
717             break;
718         case SvgLengthType::VERTICAL:
719             ContainerLength = rule.GetContainerRect().Height();
720             break;
721         /*using the original definition, radius*/
722         case SvgLengthType::OTHER:
723             ContainerLength = std::min(rule.GetContainerRect().Width(), rule.GetContainerRect().Height());
724             break;
725     }
726     auto length = ConvertDimensionToPx(origin, rule.GetViewPort(), lengthType);
727     if (rule.GetLengthScaleUnit() == SvgLengthScaleUnit::OBJECT_BOUNDING_BOX) {
728         length *= ContainerLength;
729     }
730     return length;
731 }
732 
733 float SvgNode::GetMeasuredPosition(Dimension origin, const SvgLengthScaleRule& rule,
734     SvgLengthType lengthType)
735 {
736     float offset = 0.0f;
737     float ContainerLength = 0.0f;
738     switch (lengthType) {
739         case SvgLengthType::HORIZONTAL:
740             ContainerLength = rule.GetContainerRect().Width();
741             offset = rule.GetContainerRect().Left();
742             break;
743         case SvgLengthType::VERTICAL:
744             ContainerLength = rule.GetContainerRect().Height();
745             offset = rule.GetContainerRect().Top();
746             break;
747         default:
748             return 0.0f;
749     }
750     auto position = ConvertDimensionToPx(origin, rule.GetViewPort(), lengthType);
751     if (rule.GetLengthScaleUnit() == SvgLengthScaleUnit::OBJECT_BOUNDING_BOX) {
752         position *= ContainerLength;
753         position += offset;
754     }
755     return position;
756 }
757 
758 double SvgNode::ConvertDimensionToPx(const Dimension& value, const Size& viewPort, SvgLengthType type) const
759 {
760     auto width = viewPort.Width();
761     auto height = viewPort.Height();
762     switch (value.Unit()) {
763         case DimensionUnit::PERCENT: {
764             if (type == SvgLengthType::HORIZONTAL) {
765                 return value.Value() * width;
766             }
767             if (type == SvgLengthType::VERTICAL) {
768                 return value.Value() * height;
769             }
770             if (type == SvgLengthType::OTHER) {
771                 if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN)) {
772                     return value.Value() * sqrt(width * height);
773                 }
774                 return value.Value() * std::sqrt(width * width + height * height) / std::sqrt(2.0f);
775             }
776             return 0.0;
777         }
778         case DimensionUnit::PX:
779             return value.Value();
780         default:
781             auto svgContext = svgContext_.Upgrade();
782             if (svgContext) {
783                 return svgContext->NormalizeToPx(value);
784             }
785             return 0.0;
786     }
787 }
788 
789 double SvgNode::ConvertDimensionToPx(const Dimension& value, double baseValue) const
790 {
791     if (value.Unit() == DimensionUnit::PERCENT) {
792         return value.Value() * baseValue;
793     }
794     if (value.Unit() == DimensionUnit::PX) {
795         return value.Value();
796     }
797     auto svgContext = svgContext_.Upgrade();
798     if (svgContext) {
799         return svgContext->NormalizeToPx(value);
800     }
801     return 0.0;
802 }
803 
804 std::optional<Ace::Gradient> SvgNode::GetGradient(const std::string& href)
805 {
806     auto svgContext = svgContext_.Upgrade();
807     CHECK_NULL_RETURN(svgContext, std::nullopt);
808     if (href.empty()) {
809         return std::nullopt;
810     }
811     auto refSvgNode = svgContext->GetSvgNodeById(href);
812     CHECK_NULL_RETURN(refSvgNode, std::nullopt);
813     auto svgGradient = DynamicCast<SvgGradient>(refSvgNode);
814     if (svgGradient) {
815         return std::make_optional(svgGradient->GetGradient());
816     }
817     return std::nullopt;
818 }
819 
820 Rect SvgNode::GetSvgContainerRect() const
821 {
822     auto svgContext = svgContext_.Upgrade();
823     if (!svgContext) {
824         LOGE("Gradient failed, svgContext is null");
825         static Rect empty;
826         return empty;
827     }
828     return Rect(0, 0, svgContext->GetViewPort().Width(), svgContext->GetViewPort().Height());
829 }
830 
831 const Rect& SvgNode::GetRootViewBox() const
832 {
833     auto svgContext = svgContext_.Upgrade();
834     if (!svgContext) {
835         LOGE("Gradient failed, svgContext is null");
836         static Rect empty;
837         return empty;
838     }
839     return svgContext->GetRootViewBox();
840 }
841 
842 void SvgNode::PrepareAnimation(const RefPtr<SvgAnimation>& animate)
843 {
844     auto attrName = animate->GetAttributeName();
845     if (COLOR_GETTERS.find(attrName) != COLOR_GETTERS.end()) {
846         Color originalValue = COLOR_GETTERS.find(attrName)->second(attributes_);
847         AnimateOnAttribute(animate, originalValue);
848     } else if (DIMENSION_GETTERS.find(attrName) != DIMENSION_GETTERS.end()) {
849         Dimension originalValue = DIMENSION_GETTERS.find(attrName)->second(attributes_);
850         AnimateOnAttribute(animate, originalValue);
851     } else if (DOUBLE_GETTERS.find(attrName) != DOUBLE_GETTERS.end()) {
852         double originalValue = DOUBLE_GETTERS.find(attrName)->second(attributes_);
853         AnimateOnAttribute(animate, originalValue);
854     } else if (attrName.find(TRANSFORM) != std::string::npos) {
855         AnimateTransform(animate, 0.0f);
856     } else {
857         LOGW("animation attrName not valid: %s", attrName.c_str());
858     }
859 }
860 
861 // create animation callback
862 template<typename T>
863 void SvgNode::AnimateOnAttribute(const RefPtr<SvgAnimation>& animate, const T& originalValue)
864 {
865     std::function<void(T)> callback;
866     callback = [weak = WeakClaim(this), attrName = animate->GetAttributeName()](T value) {
867         auto self = weak.Upgrade();
868         CHECK_NULL_VOID(self);
869         self->UpdateAttr(attrName, value);
870         auto context = self->svgContext_.Upgrade();
871         CHECK_NULL_VOID(context);
872         context->AnimateFlush();
873     };
874     animate->CreatePropertyAnimation(originalValue, std::move(callback));
875 }
876 
877 // update attribute for svgNode and its children
878 void SvgNode::UpdateAttrHelper(const std::string& name, const std::string& val)
879 {
880     SetAttr(name, val);
881     if (!passStyle_) {
882         return;
883     }
884     for (auto&& child : children_) {
885         if (child->inheritStyle_) {
886             child->UpdateAttrHelper(name, val);
887         }
888     }
889 }
890 
891 template<typename T>
892 void SvgNode::UpdateAttr(const std::string& /* name */, const T& /* val */)
893 {
894     LOGW("data type not supported");
895 }
896 
897 template<>
898 void SvgNode::UpdateAttr(const std::string& name, const Color& val)
899 {
900     UpdateAttrHelper(name, val.ColorToString());
901 }
902 
903 template<>
904 void SvgNode::UpdateAttr(const std::string& name, const Dimension& val)
905 {
906     UpdateAttrHelper(name, val.ToString());
907 }
908 
909 template<>
910 void SvgNode::UpdateAttr(const std::string& name, const double& val)
911 {
912     UpdateAttrHelper(name, std::to_string(val));
913 }
914 
915 void SvgNode::AnimateTransform(const RefPtr<SvgAnimation>& animate, double originalValue)
916 {
917     if (!animate->GetValues().empty()) {
918         AnimateFrameTransform(animate, originalValue);
919     } else {
920         AnimateFromToTransform(animate, originalValue);
921     }
922 }
923 
924 void SvgNode::AnimateFrameTransform(const RefPtr<SvgAnimation>& animate, double originalValue)
925 {
926     std::vector<std::vector<float>> frames;
927     std::string type;
928     if (!animate->GetFrames(frames, type)) {
929         LOGE("invalid animate keys info of type %{public}s", type.c_str());
930         return;
931     }
932     if (frames.size() <= 1) {
933         LOGE("invalid frames numbers %{public}s", type.c_str());
934         return;
935     }
936 
937     // change Values to frame indices to create an index-based animation
938     // property values of each frame are stored in [frames]
939     std::vector<std::string> indices;
940     uint32_t size = animate->GetValues().size();
941     for (uint32_t i = 0; i < size; i++) {
942         indices.emplace_back(std::to_string(i));
943     }
944     animate->SetValues(indices);
945 
946     std::function<void(double)> callback = [weak = WeakClaim(this), type, frames](double value) {
947         auto self = weak.Upgrade();
948         CHECK_NULL_VOID(self);
949         // use index and rate to locate frame and progress
950         auto index = static_cast<uint32_t>(value);
951         double rate = value - index;
952         if (index >= frames.size() - 1) {
953             index = frames.size() - 2;
954             rate = 1.0;
955         }
956         if (!SvgTransform::SetProperty(type, frames[index], frames[index + 1], rate, self->animateTransform_)) {
957             LOGE("set property failed: property %{public}s not in map", type.c_str());
958             return;
959         }
960         auto context = self->svgContext_.Upgrade();
961         CHECK_NULL_VOID(context);
962         context->AnimateFlush();
963     };
964     animate->CreatePropertyAnimation(originalValue, std::move(callback));
965 }
966 
967 void SvgNode::AnimateFromToTransform(const RefPtr<SvgAnimation>& animate, double originalValue)
968 {
969     std::vector<float> fromVec;
970     std::vector<float> toVec;
971     std::string type;
972     if (!animate->GetValuesRange(fromVec, toVec, type)) {
973         LOGE("invalid animate info of type %{public}s", type.c_str());
974         return;
975     }
976 
977     std::function<void(double)> callback = [weak = WeakClaim(this), type, fromVec, toVec](double value) {
978         auto self = weak.Upgrade();
979         CHECK_NULL_VOID(self);
980         if (!SvgTransform::SetProperty(type, fromVec, toVec, value, self->animateTransform_)) {
981             LOGE("set property failed: property %{public}s not in map", type.c_str());
982             return;
983         }
984         auto context = self->svgContext_.Upgrade();
985         CHECK_NULL_VOID(context);
986         context->AnimateFlush();
987     };
988     animate->CreatePropertyAnimation(originalValue, std::move(callback));
989 }
990 
991 Offset SvgNode::CalcGlobalPivot(const std::pair<Dimension, Dimension>& transformOrigin,
992     const SvgLengthScaleRule& lengthRule)
993 {
994     auto x = GetRegionLength(transformOrigin.first, lengthRule, SvgLengthType::HORIZONTAL);
995     auto y = GetRegionLength(transformOrigin.second, lengthRule, SvgLengthType::VERTICAL);
996     return Offset(x, y);
997 }
998 
999 SvgLengthScaleRule SvgNode::BuildContentScaleRule(const SvgCoordinateSystemContext& parentContext,
1000     SvgLengthScaleUnit contentUnits)
1001 {
1002     if (contentUnits == SvgLengthScaleUnit::USER_SPACE_ON_USE) {
1003         return parentContext.BuildScaleRule(SvgLengthScaleUnit::USER_SPACE_ON_USE);
1004     }
1005     // create default rect to draw graphic
1006     auto squareWH = std::min(parentContext.GetContainerRect().Width(), parentContext.GetContainerRect().Height());
1007     Rect defaultRect(0, 0, squareWH, squareWH);
1008     SvgLengthScaleRule ContentRule (defaultRect,
1009         parentContext.GetViewPort(), SvgLengthScaleUnit::OBJECT_BOUNDING_BOX);
1010     return ContentRule;
1011 }
1012 
1013 void SvgNode::TransformForCurrentOBB(RSCanvas& canvas, const SvgLengthScaleRule& contentRule,
1014     const Size& ContainerSize, const Offset& offset)
1015 {
1016     if (contentRule.GetLengthScaleUnit() == SvgLengthScaleUnit::USER_SPACE_ON_USE) {
1017         return;
1018     }
1019     float scaleX = 0.0f;
1020     float scaleY = 0.0f;
1021     float translateX = 0.0f;
1022     float translateY = 0.0f;
1023     SvgPreserveAspectRatio preserveAspectRatio;
1024     preserveAspectRatio.svgAlign = SvgAlign::ALIGN_NONE;
1025     SvgAttributesParser::ComputeScale(contentRule.GetContainerRect().GetSize(), ContainerSize,
1026         preserveAspectRatio, scaleX, scaleY);
1027     SvgAttributesParser::ComputeTranslate(contentRule.GetContainerRect().GetSize(),
1028         ContainerSize, scaleX, scaleY, preserveAspectRatio.svgAlign,
1029         translateX, translateY);
1030     // scale the graphic content of the given element non-uniformly
1031     canvas.Translate(translateX  + offset.GetX(), translateY + offset.GetY());
1032     canvas.Scale(scaleX, scaleY);
1033 }
1034 
1035 void SvgNode::ApplyTransform(RSRecordingPath& path, const SvgLengthScaleRule& lengthRule)
1036 {
1037     if (!attributes_.transformVec.size()) {
1038         return;
1039     }
1040     Offset globalPivot = CalcGlobalPivot(attributes_.transformOrigin, lengthRule);
1041     auto matrix = NGSvgTransform::CreateMatrix4(attributes_.transformVec, globalPivot, lengthRule);
1042     path.Transform(RosenSvgPainter::ToDrawingMatrix(matrix));
1043 }
1044 } // namespace OHOS::Ace::NG