1 /*
2 * Copyright (c) 2024 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 "frameworks/core/components_ng/svg/parse/svg_image.h"
17
18 #include "base/utils/utils.h"
19 #include "base/base64/base64_util.h"
20 #include "core/image/image_source_info.h"
21
22 namespace OHOS::Ace::NG {
23
SvgImage()24 SvgImage::SvgImage() : SvgNode() {}
25
Create()26 RefPtr<SvgNode> SvgImage::Create()
27 {
28 return AceType::MakeRefPtr<SvgImage>();
29 }
30
OnDraw(RSCanvas & canvas,const Size & viewPort,const std::optional<Color> & color)31 void SvgImage::OnDraw(RSCanvas& canvas, const Size& viewPort, const std::optional<Color>& color)
32 {
33 if (imageAttr_.href.empty()) {
34 LOGW("Svg image href is empty");
35 return;
36 }
37
38 auto x = ConvertDimensionToPx(imageAttr_.x, viewPort, SvgLengthType::HORIZONTAL);
39 auto y = ConvertDimensionToPx(imageAttr_.y, viewPort, SvgLengthType::VERTICAL);
40 auto width = ConvertDimensionToPx(imageAttr_.width, viewPort, SvgLengthType::HORIZONTAL);
41 auto height = ConvertDimensionToPx(imageAttr_.height, viewPort, SvgLengthType::VERTICAL);
42 if (LessOrEqual(width, 0.0f) || LessOrEqual(height, 0.0f)) {
43 LOGW("Svg image size is illegal");
44 return;
45 }
46
47 auto srcType = ParseHrefAttr(imageAttr_.href);
48 auto data = std::make_shared<RSData>();
49 switch (srcType) {
50 case SrcType::BASE64:
51 data = LoadBase64Image(imageAttr_.href);
52 break;
53 case SrcType::ASSET:
54 data = LoadLocalImage(imageAttr_.href);
55 break;
56 default:
57 LOGW("Unknown svg href src type");
58 }
59 CHECK_NULL_VOID(data);
60 RSImage image;
61 image.MakeFromEncoded(data);
62 auto dstRect = CalcDstRect(Size(image.GetWidth(), image.GetHeight()), Rect(x, y, width, height));
63 canvas.DrawImageRect(image, dstRect, RSSamplingOptions());
64 }
65
LoadLocalImage(const std::string & uri)66 std::shared_ptr<RSData> SvgImage::LoadLocalImage(const std::string& uri)
67 {
68 std::string svgPath = GetImagePath();
69 auto realPath = uri;
70 auto dotPos = realPath.find_last_of('.');
71 auto format = realPath.substr(dotPos + 1);
72 if (format == "svg" || format == "gif") {
73 LOGW("Svg image format is not supported");
74 return nullptr;
75 }
76 auto pos = svgPath.find_last_of('/');
77 if (pos != std::string::npos) {
78 realPath = svgPath.substr(0, pos + 1) + uri;
79 }
80
81 std::string assetSrc(realPath);
82 if (assetSrc[0] == '/') {
83 assetSrc = assetSrc.substr(1); // get the asset src without '/'.
84 } else if (assetSrc[0] == '.' && assetSrc.size() > 2 && assetSrc[1] == '/') { // 2 : min length
85 assetSrc = assetSrc.substr(2); // 2 : get the asset src without './'.
86 }
87
88 auto pipelineContext = PipelineContext::GetCurrentContext();
89 if (!pipelineContext) {
90 LOGW("invalid pipeline context");
91 return nullptr;
92 }
93 auto assetManager = pipelineContext->GetAssetManager();
94 if (!assetManager) {
95 LOGW("No asset manager!");
96 return nullptr;
97 }
98
99 auto assetData = assetManager->GetAsset(assetSrc);
100 if (!assetData) {
101 LOGW("No asset data!");
102 return nullptr;
103 }
104 const uint8_t* data = assetData->GetData();
105 const size_t dataSize = assetData->GetSize();
106
107 auto drawingData = std::make_shared<RSData>();
108 if (!drawingData->BuildWithCopy(data, dataSize)) {
109 LOGW("Load local svg image failed!");
110 }
111 return drawingData;
112 }
113
LoadBase64Image(const std::string & uri)114 std::shared_ptr<RSData> SvgImage::LoadBase64Image(const std::string& uri)
115 {
116 std::string dst;
117 auto iter = uri.find("base64");
118 std::string content = uri.substr(iter + 7);
119 Base64Util::Decode(content, dst);
120
121 auto data = std::make_shared<RSData>();
122 if (!data->BuildWithCopy(dst.c_str(), dst.length())) {
123 LOGW("Load base64 svg image failed!");
124 }
125 return data;
126 }
127
ParseHrefAttr(const std::string & uri)128 SrcType SvgImage::ParseHrefAttr(const std::string& uri)
129 {
130 if (uri.empty()) {
131 return SrcType::UNSUPPORTED;
132 }
133 auto iter = uri.find_first_of(':');
134 if (iter == std::string::npos) {
135 return SrcType::ASSET;
136 }
137 std::string head = uri.substr(0, iter);
138 std::transform(head.begin(), head.end(), head.begin(), [](unsigned char c) { return std::tolower(c); });
139 if (head == "http" || head == "https") {
140 return SrcType::NETWORK;
141 } else if (head == "data") {
142 static constexpr char BASE64_PATTERN[] =
143 "^data:image/(jpeg|JPEG|jpg|JPG|png|PNG|ico|ICO|gif|GIF|bmp|BMP|webp|WEBP);base64$";
144 if (ImageSourceInfo::IsValidBase64Head(uri, BASE64_PATTERN)) {
145 return SrcType::BASE64;
146 }
147 return SrcType::UNSUPPORTED;
148 } else {
149 return SrcType::ASSET;
150 }
151 }
152
CalcDstRect(const Size & realSize,const Rect & viewBox)153 RSRect SvgImage::CalcDstRect(const Size& realSize, const Rect& viewBox)
154 {
155 if (NearEqual(realSize.Width(), 0.0f) || NearEqual(realSize.Height(), 0.0f)) {
156 return RSRect(0, 0, 0, 0);
157 }
158 auto scaleValue = std::min(viewBox.Width() / realSize.Width(), viewBox.Height() / realSize.Height());
159 auto spaceX = viewBox.Width() - realSize.Width() * scaleValue;
160 auto spaceY = viewBox.Height() - realSize.Height() * scaleValue;
161 auto offsetX = viewBox.Left() + spaceX * 0.5f; // 0.5f Align Center
162 auto offsetY = viewBox.Top() + spaceY * 0.5f; // 0.5f Align Center
163 return RSRect(offsetX, offsetY, realSize.Width() * scaleValue + offsetX, realSize.Height() * scaleValue + offsetY);
164 }
165
ParseAndSetSpecializedAttr(const std::string & name,const std::string & value)166 bool SvgImage::ParseAndSetSpecializedAttr(const std::string& name, const std::string& value)
167 {
168 static const LinearMapNode<void (*)(const std::string&, SvgImageAttribute&)> attrs[] = {
169 { DOM_SVG_HEIGHT,
170 [](const std::string& val, SvgImageAttribute& attr) {
171 attr.height = SvgAttributesParser::ParseDimension(val);
172 } },
173 { DOM_SVG_HREF,
174 [](const std::string& val, SvgImageAttribute& attr) {
175 attr.href = val;
176 } },
177 { DOM_SVG_WIDTH,
178 [](const std::string& val, SvgImageAttribute& attr) {
179 attr.width = SvgAttributesParser::ParseDimension(val);
180 } },
181 { DOM_SVG_X,
182 [](const std::string& val, SvgImageAttribute& attr) {
183 attr.x = SvgAttributesParser::ParseDimension(val);
184 } },
185 { DOM_SVG_XLINK_HREF,
186 [](const std::string& val, SvgImageAttribute& attr) {
187 attr.href = val;
188 } },
189 { DOM_SVG_Y,
190 [](const std::string& val, SvgImageAttribute& attr) {
191 attr.y = SvgAttributesParser::ParseDimension(val);
192 } },
193 };
194 std::string key = name;
195 StringUtils::TransformStrCase(key, StringUtils::TEXT_CASE_LOWERCASE);
196 auto attrIter = BinarySearchFindIndex(attrs, ArraySize(attrs), key.c_str());
197 if (attrIter != -1) {
198 attrs[attrIter].value(value, imageAttr_);
199 return true;
200 }
201 return false;
202 }
203
204 } // namespace OHOS::Ace::NG
205