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