1 /*
2 * Copyright (c) 2021-2022 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/font/rosen_font_loader.h"
17
18 #include "base/network/download_manager.h"
19 #include "core/common/resource/resource_manager.h"
20 #include "core/common/resource/resource_wrapper.h"
21 #include "core/components/font/rosen_font_collection.h"
22 #include "core/components_ng/base/ui_node.h"
23 #include "core/pipeline/base/rosen_render_context.h"
24
25 namespace OHOS::Ace {
26
27 constexpr size_t FILE_HEAD_LENGTH = 7; // 7 is the size of "file://"
28 constexpr size_t MEMORY_HEAD_LENGTH = 9; // 9 is the size of "memory://"
29 constexpr size_t INTERNAL_FILE_HEAD_LENGTH = 15; // 15 is the size of "internal://app/"
30
31 const std::regex RAWFILE_APP_RES_PATH_REGEX(R"(^resource://RAWFILE/(.*)$)");
32 constexpr uint32_t RAWFILE_RESOURCE_MATCH_SIZE = 2;
33
RosenFontLoader(const std::string & familyName,const std::string & familySrc)34 RosenFontLoader::RosenFontLoader(const std::string& familyName, const std::string& familySrc)
35 : FontLoader(familyName, familySrc)
36 {}
RosenFontLoader(const std::string & familyName,const std::vector<std::string> & familySrcArray)37 RosenFontLoader::RosenFontLoader(const std::string& familyName, const std::vector<std::string>& familySrcArray)
38 : FontLoader(familyName, familySrcArray)
39 {}
40
AddFont(const RefPtr<PipelineBase> & context,const std::string & bundleName,const std::string & moduleName)41 void RosenFontLoader::AddFont(
42 const RefPtr<PipelineBase>& context, const std::string& bundleName, const std::string& moduleName)
43 {
44 if (context == nullptr || familySrc_.empty()) {
45 TAG_LOGW(AceLogTag::ACE_FONT, "AddFont familySrc is empty:%{public}d", familySrc_.empty());
46 return;
47 }
48
49 if (familySrc_.substr(0, strlen(FONT_SRC_NETWORK)) == FONT_SRC_NETWORK) {
50 // Get font from NetWork.
51 LoadFromNetwork(context);
52 } else if (familySrc_.substr(0, strlen(FONT_SRC_RESOURCE)) == FONT_SRC_RESOURCE) {
53 // Get font from Resource.
54 LoadFromResource(context, bundleName, moduleName);
55 } else if (familySrc_.find_first_of(':') != std::string::npos) {
56 // Get font from file
57 // Read file with absolute path to solve the problem that rawfile file registration fails during preview.
58 LoadFromFile(context);
59 } else {
60 // Get font from asset.
61 LoadFromAsset(context);
62 }
63 }
64
SetDefaultFontFamily(const char * fontFamily,const std::vector<std::string> & familySrc)65 void RosenFontLoader::SetDefaultFontFamily(const char* fontFamily, const std::vector<std::string>& familySrc)
66 {
67 RosenFontCollection::GetInstance().LoadFontFamily(fontFamily, familySrc);
68 }
69
LoadFromNetwork(const RefPtr<PipelineBase> & context)70 void RosenFontLoader::LoadFromNetwork(const RefPtr<PipelineBase>& context)
71 {
72 auto weakContext = AceType::WeakClaim(AceType::RawPtr(context));
73 context->GetTaskExecutor()->PostTask(
74 [weak = AceType::WeakClaim(this), weakContext] {
75 auto fontLoader = weak.Upgrade();
76 auto context = weakContext.Upgrade();
77 if (!fontLoader || !context) {
78 return;
79 }
80 TAG_LOGI(AceLogTag::ACE_FONT, "begin to load font from network.");
81 DownloadCallback downloadCallback;
82 downloadCallback.successCallback = [weak, weakContext](
83 const std::string&& imageData, bool async, int32_t instanceId) {
84 ContainerScope scope(instanceId);
85 auto context = weakContext.Upgrade();
86 CHECK_NULL_VOID(context);
87 context->GetTaskExecutor()->PostTask(
88 [imageData, weak] {
89 auto fontLoader = weak.Upgrade();
90 CHECK_NULL_VOID(fontLoader);
91 // Load font.
92 RosenFontCollection::GetInstance().LoadFontFromList(
93 reinterpret_cast<const uint8_t*>(imageData.c_str()), imageData.size(),
94 fontLoader->familyName_);
95 fontLoader->isLoaded_ = true;
96 // When font is already loaded, notify all which used this font.
97 fontLoader->NotifyCallbacks();
98 },
99 TaskExecutor::TaskType::UI, "ArkUIFontLoadFromList");
100 };
101 downloadCallback.failCallback = [](std::string errorMessage, ImageErrorInfo /* errorInfo */, bool async,
102 int32_t instanceId) {
103 TAG_LOGW(
104 AceLogTag::ACE_FONT, "Sync Download font Failed,errorMessage is %{public}s", errorMessage.c_str());
105 };
106 downloadCallback.cancelCallback = downloadCallback.failCallback;
107 if (!DownloadManager::GetInstance()->DownloadSync(std::move(downloadCallback),
108 fontLoader->familySrc_, context->GetInstanceId(), -1)) {
109 return;
110 }
111 },
112 TaskExecutor::TaskType::BACKGROUND, "ArkUIFontLoadFromNetwork");
113 }
114
LoadFromFile(const RefPtr<PipelineBase> & context)115 void RosenFontLoader::LoadFromFile(const RefPtr<PipelineBase>& context)
116 {
117 auto weakContext = AceType::WeakClaim(AceType::RawPtr(context));
118 context->GetTaskExecutor()->PostTask(
119 [weak = AceType::WeakClaim(this), weakContext] {
120 auto fontLoader = weak.Upgrade();
121 auto context = weakContext.Upgrade();
122 if (!fontLoader || !context) {
123 return;
124 }
125
126 auto filePath = fontLoader->RemovePathHead(fontLoader->familySrc_);
127 if (filePath.length() > PATH_MAX) {
128 TAG_LOGW(AceLogTag::ACE_FONT, "src path is too long");
129 return;
130 }
131 TAG_LOGI(AceLogTag::ACE_FONT, "begin to load font from file.");
132
133 auto assetData = fontLoader->GetAssetFromFile(filePath);
134 if (!assetData) {
135 TAG_LOGW(AceLogTag::ACE_FONT, "No asset data!");
136 return;
137 }
138 const std::vector<uint8_t> fontData(assetData->GetData(), assetData->GetData() + assetData->GetSize());
139 fontLoader->PostLoadFontTask(std::move(fontData), context);
140 },
141 TaskExecutor::TaskType::BACKGROUND, "ArkUIFontLoadFromFile");
142 }
143
GetAssetFromFile(const std::string & fileName) const144 RefPtr<Asset> RosenFontLoader::GetAssetFromFile(const std::string& fileName) const
145 {
146 errno = 0;
147 char realPath[PATH_MAX] = { 0x00 };
148 if (!RealPath(fileName, realPath)) {
149 return nullptr;
150 }
151 auto fp = std::fopen(realPath, "rb");
152 if (!fp) {
153 TAG_LOGW(AceLogTag::ACE_FONT, "[%{private}s] open file error %{public}s", fileName.c_str(), strerror(errno));
154 return nullptr;
155 }
156
157 if (std::fseek(fp, 0, SEEK_END) != 0) {
158 TAG_LOGW(
159 AceLogTag::ACE_FONT, "[%{private}s] seek file tail error %{public}s", fileName.c_str(), strerror(errno));
160 std::fclose(fp);
161 return nullptr;
162 }
163
164 size_t size = std::ftell(fp);
165 if (size < 0) {
166 TAG_LOGW(AceLogTag::ACE_FONT, "[%{private}s] tell file error %{public}s", fileName.c_str(), strerror(errno));
167 std::fclose(fp);
168 return nullptr;
169 }
170
171 auto data = std::make_unique<char[]>(size);
172 if (data == nullptr) {
173 TAG_LOGW(AceLogTag::ACE_FONT, "[%{private}s] new uint8_t array failed", fileName.c_str());
174 std::fclose(fp);
175 return nullptr;
176 }
177
178 if (std::fseek(fp, 0, SEEK_SET) != 0) {
179 TAG_LOGW(
180 AceLogTag::ACE_FONT, "[%{private}s] seek file begin error %{public}s", fileName.c_str(), strerror(errno));
181 std::fclose(fp);
182 return nullptr;
183 }
184
185 auto rsize = std::fread(data.get(), 1, size, fp);
186 if (rsize <= 0) {
187 TAG_LOGW(AceLogTag::ACE_FONT, "[%{private}s] read file failed, %{public}s", fileName.c_str(), strerror(errno));
188 std::fclose(fp);
189 return nullptr;
190 }
191 std::fclose(fp);
192 TAG_LOGI(
193 AceLogTag::ACE_FONT, "[%{private}s] length: %{public}zu/%{public}zu success", fileName.c_str(), rsize, size);
194 return AceType::MakeRefPtr<RSAsset>(std::move(data), rsize);
195 }
196
RemovePathHead(const std::string & uri)197 std::string RosenFontLoader::RemovePathHead(const std::string& uri)
198 {
199 auto iter = uri.find_first_of(':');
200 if (iter == std::string::npos) {
201 TAG_LOGI(AceLogTag::ACE_FONT, "No need RemovePathHead.");
202 return uri;
203 }
204 std::string head = uri.substr(0, iter);
205 if ((head == "file" && uri.size() > FILE_HEAD_LENGTH) || (head == "memory" && uri.size() > MEMORY_HEAD_LENGTH) ||
206 (head == "internal" && uri.size() > INTERNAL_FILE_HEAD_LENGTH)) {
207 // the file uri format is like "file:///data/data...",
208 // the memory uri format is like "memory://font.ttf" for example,
209 // iter + 3 to get the absolutely file path substring : "/data/data..." or the font file name: "font.ttf"
210 auto startIndex = iter + 3;
211 startIndex = std::clamp(startIndex, static_cast<size_t>(0), uri.length());
212 return uri.substr(startIndex);
213 }
214 TAG_LOGW(AceLogTag::ACE_FONT, "Wrong scheme, not a valid File!");
215 return std::string();
216 }
217
LoadFromResource(const RefPtr<PipelineBase> & context,const std::string & bundleName,const std::string & moduleName)218 void RosenFontLoader::LoadFromResource(
219 const RefPtr<PipelineBase>& context, const std::string& bundleName, const std::string& moduleName)
220 {
221 auto weakContext = AceType::WeakClaim(AceType::RawPtr(context));
222 context->GetTaskExecutor()->PostTask(
223 [weak = AceType::WeakClaim(this), weakContext, bundleName, moduleName] {
224 auto fontLoader = weak.Upgrade();
225 auto context = weakContext.Upgrade();
226 if (!fontLoader || !context) {
227 return;
228 }
229 auto resourceObject = AceType::MakeRefPtr<ResourceObject>(bundleName, moduleName, context->GetInstanceId());
230 RefPtr<ResourceAdapter> resourceAdapter = nullptr;
231 RefPtr<ThemeConstants> themeConstants = nullptr;
232 if (SystemProperties::GetResourceDecoupling()) {
233 resourceAdapter = ResourceManager::GetInstance().GetOrCreateResourceAdapter(resourceObject);
234 CHECK_NULL_VOID(resourceAdapter);
235 } else {
236 auto themeManager = PipelineBase::CurrentThemeManager();
237 CHECK_NULL_VOID(themeManager);
238 themeConstants = themeManager->GetThemeConstants();
239 CHECK_NULL_VOID(themeConstants);
240 }
241 auto resourceWrapper = AceType::MakeRefPtr<ResourceWrapper>(themeConstants, resourceAdapter);
242 std::string rawFile;
243 std::unique_ptr<uint8_t[]> data;
244 size_t dataLen = 0;
245 std::smatch matches;
246 if (std::regex_match(fontLoader->familySrc_, matches, RAWFILE_APP_RES_PATH_REGEX) &&
247 matches.size() == RAWFILE_RESOURCE_MATCH_SIZE) {
248 rawFile = matches[1].str();
249 }
250 if (rawFile.empty()) {
251 TAG_LOGW(AceLogTag::ACE_FONT, "LoadFromResource rawFile is empty");
252 return;
253 }
254
255 if (!resourceWrapper->GetRawFileData(rawFile, dataLen, data, bundleName, moduleName) || !data.get()) {
256 TAG_LOGW(AceLogTag::ACE_FONT, "Get font data by rawFile failed, src:%{private}s, rawFile:%{public}s",
257 fontLoader->familySrc_.c_str(), rawFile.c_str());
258 return;
259 }
260
261 const std::vector<uint8_t> fontData(data.get(), data.get() + dataLen);
262 fontLoader->PostLoadFontTask(std::move(fontData), context);
263 },
264 TaskExecutor::TaskType::BACKGROUND, "ArkUIFontLoadFromResource");
265 }
266
LoadFromAsset(const RefPtr<PipelineBase> & context)267 void RosenFontLoader::LoadFromAsset(const RefPtr<PipelineBase>& context)
268 {
269 auto weakContext = AceType::WeakClaim(AceType::RawPtr(context));
270 context->GetTaskExecutor()->PostTask(
271 [weak = AceType::WeakClaim(this), weakContext] {
272 auto fontLoader = weak.Upgrade();
273 auto context = weakContext.Upgrade();
274 if (!fontLoader || !context) {
275 return;
276 }
277 auto assetManager = context->GetAssetManager();
278 if (!assetManager) {
279 return;
280 }
281 TAG_LOGI(AceLogTag::ACE_FONT, "begin to load font from asset.");
282 std::string assetSrc(fontLoader->familySrc_);
283 if (assetSrc[0] == '/') {
284 assetSrc = assetSrc.substr(1); // get the asset src without '/'.
285 } else if (assetSrc[0] == '.' && assetSrc.size() > 2 && assetSrc[1] == '/') {
286 assetSrc = assetSrc.substr(2); // get the asset src without './'.
287 }
288 auto assetData = assetManager->GetAsset(assetSrc);
289 if (!assetData) {
290 TAG_LOGW(AceLogTag::ACE_FONT, "No asset data!");
291 return;
292 }
293 const std::vector<uint8_t> fontData(assetData->GetData(), assetData->GetData() + assetData->GetSize());
294 fontLoader->PostLoadFontTask(std::move(fontData), context);
295 },
296 TaskExecutor::TaskType::BACKGROUND, "ArkUIFontLoadFromAsset");
297 }
298
PostLoadFontTask(const std::vector<uint8_t> & fontData,const RefPtr<PipelineBase> & context)299 void RosenFontLoader::PostLoadFontTask(const std::vector<uint8_t>& fontData, const RefPtr<PipelineBase>& context)
300 {
301 CHECK_NULL_VOID(context);
302 context->GetTaskExecutor()->PostTask(
303 [fontData, weak = AceType::WeakClaim(this)] {
304 auto fontLoader = weak.Upgrade();
305 CHECK_NULL_VOID(fontLoader);
306 // Load font.
307 RosenFontCollection::GetInstance().LoadFontFromList(
308 fontData.data(), fontData.size(), fontLoader->familyName_);
309 fontLoader->isLoaded_ = true;
310
311 // When font is already loaded, notify all which used this font.
312 fontLoader->NotifyCallbacks();
313 },
314 TaskExecutor::TaskType::UI, "ArkUIFontLoadFromList");
315 }
316
NotifyCallbacks()317 void RosenFontLoader::NotifyCallbacks()
318 {
319 for (const auto& [node, callback] : callbacks_) {
320 if (callback) {
321 callback();
322 }
323 }
324 callbacks_.clear();
325 std::stringstream ssNodes;
326 ssNodes << "[";
327 for (const auto& [node, callback] : callbacksNG_) {
328 auto uiNode = node.Upgrade();
329 if (uiNode) {
330 ssNodes << std::to_string(uiNode->GetId());
331 ssNodes << ", ";
332 }
333 if (callback) {
334 callback();
335 }
336 }
337 ssNodes << "]";
338 ACE_SCOPED_TRACE("Load Success! NotifyCallbacks [familyName:%s][size:%d][nodes:%s]", familyName_.c_str(),
339 static_cast<uint32_t>(callbacksNG_.size()), ssNodes.str().c_str());
340 TAG_LOGI(AceLogTag::ACE_FONT,
341 "Load Success! NotifyCallbacks [familyName:%{public}s][size:%{public}d][nodes:%{public}s]", familyName_.c_str(),
342 static_cast<uint32_t>(callbacksNG_.size()), ssNodes.str().c_str());
343 callbacksNG_.clear();
344 if (variationChanged_) {
345 variationChanged_();
346 }
347 }
348
349 } // namespace OHOS::Ace
350