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, bool async, int32_t instanceId) {
102 TAG_LOGW(
103 AceLogTag::ACE_FONT, "Sync Download font Failed,errorMessage is %{public}s", errorMessage.c_str());
104 };
105 downloadCallback.cancelCallback = downloadCallback.failCallback;
106 if (!DownloadManager::GetInstance()->DownloadSync(std::move(downloadCallback),
107 fontLoader->familySrc_, context->GetInstanceId(), -1)) {
108 return;
109 }
110 },
111 TaskExecutor::TaskType::BACKGROUND, "ArkUIFontLoadFromNetwork");
112 }
113
LoadFromFile(const RefPtr<PipelineBase> & context)114 void RosenFontLoader::LoadFromFile(const RefPtr<PipelineBase>& context)
115 {
116 auto weakContext = AceType::WeakClaim(AceType::RawPtr(context));
117 context->GetTaskExecutor()->PostTask(
118 [weak = AceType::WeakClaim(this), weakContext] {
119 auto fontLoader = weak.Upgrade();
120 auto context = weakContext.Upgrade();
121 if (!fontLoader || !context) {
122 return;
123 }
124
125 auto filePath = fontLoader->RemovePathHead(fontLoader->familySrc_);
126 if (filePath.length() > PATH_MAX) {
127 TAG_LOGW(AceLogTag::ACE_FONT, "src path is too long");
128 return;
129 }
130 TAG_LOGI(AceLogTag::ACE_FONT, "begin to load font from file.");
131
132 auto assetData = fontLoader->GetAssetFromFile(filePath);
133 if (!assetData) {
134 TAG_LOGW(AceLogTag::ACE_FONT, "No asset data!");
135 return;
136 }
137 const std::vector<uint8_t> fontData(assetData->GetData(), assetData->GetData() + assetData->GetSize());
138 fontLoader->PostLoadFontTask(std::move(fontData), context);
139 },
140 TaskExecutor::TaskType::BACKGROUND, "ArkUIFontLoadFromFile");
141 }
142
GetAssetFromFile(const std::string & fileName) const143 RefPtr<Asset> RosenFontLoader::GetAssetFromFile(const std::string& fileName) const
144 {
145 errno = 0;
146 char realPath[PATH_MAX] = { 0x00 };
147 if (!RealPath(fileName, realPath)) {
148 return nullptr;
149 }
150 auto fp = std::fopen(realPath, "rb");
151 if (!fp) {
152 TAG_LOGW(AceLogTag::ACE_FONT, "[%{private}s] open file error %{public}s", fileName.c_str(), strerror(errno));
153 return nullptr;
154 }
155
156 if (std::fseek(fp, 0, SEEK_END) != 0) {
157 TAG_LOGW(
158 AceLogTag::ACE_FONT, "[%{private}s] seek file tail error %{public}s", fileName.c_str(), strerror(errno));
159 std::fclose(fp);
160 return nullptr;
161 }
162
163 size_t size = std::ftell(fp);
164 if (size < 0) {
165 TAG_LOGW(AceLogTag::ACE_FONT, "[%{private}s] tell file error %{public}s", fileName.c_str(), strerror(errno));
166 std::fclose(fp);
167 return nullptr;
168 }
169
170 auto data = std::make_unique<char[]>(size);
171 if (data == nullptr) {
172 TAG_LOGW(AceLogTag::ACE_FONT, "[%{private}s] new uint8_t array failed", fileName.c_str());
173 std::fclose(fp);
174 return nullptr;
175 }
176
177 if (std::fseek(fp, 0, SEEK_SET) != 0) {
178 TAG_LOGW(
179 AceLogTag::ACE_FONT, "[%{private}s] seek file begin error %{public}s", fileName.c_str(), strerror(errno));
180 std::fclose(fp);
181 return nullptr;
182 }
183
184 auto rsize = std::fread(data.get(), 1, size, fp);
185 if (rsize <= 0) {
186 TAG_LOGW(AceLogTag::ACE_FONT, "[%{private}s] read file failed, %{public}s", fileName.c_str(), strerror(errno));
187 std::fclose(fp);
188 return nullptr;
189 }
190 std::fclose(fp);
191 TAG_LOGI(
192 AceLogTag::ACE_FONT, "[%{private}s] length: %{public}zu/%{public}zu success", fileName.c_str(), rsize, size);
193 return AceType::MakeRefPtr<RSAsset>(std::move(data), rsize);
194 }
195
RemovePathHead(const std::string & uri)196 std::string RosenFontLoader::RemovePathHead(const std::string& uri)
197 {
198 auto iter = uri.find_first_of(':');
199 if (iter == std::string::npos) {
200 TAG_LOGI(AceLogTag::ACE_FONT, "No need RemovePathHead.");
201 return uri;
202 }
203 std::string head = uri.substr(0, iter);
204 if ((head == "file" && uri.size() > FILE_HEAD_LENGTH) || (head == "memory" && uri.size() > MEMORY_HEAD_LENGTH) ||
205 (head == "internal" && uri.size() > INTERNAL_FILE_HEAD_LENGTH)) {
206 // the file uri format is like "file:///data/data...",
207 // the memory uri format is like "memory://font.ttf" for example,
208 // iter + 3 to get the absolutely file path substring : "/data/data..." or the font file name: "font.ttf"
209 auto startIndex = iter + 3;
210 startIndex = std::clamp(startIndex, static_cast<size_t>(0), uri.length());
211 return uri.substr(startIndex);
212 }
213 TAG_LOGW(AceLogTag::ACE_FONT, "Wrong scheme, not a valid File!");
214 return std::string();
215 }
216
LoadFromResource(const RefPtr<PipelineBase> & context,const std::string & bundleName,const std::string & moduleName)217 void RosenFontLoader::LoadFromResource(
218 const RefPtr<PipelineBase>& context, const std::string& bundleName, const std::string& moduleName)
219 {
220 auto weakContext = AceType::WeakClaim(AceType::RawPtr(context));
221 context->GetTaskExecutor()->PostTask(
222 [weak = AceType::WeakClaim(this), weakContext, bundleName, moduleName] {
223 auto fontLoader = weak.Upgrade();
224 auto context = weakContext.Upgrade();
225 if (!fontLoader || !context) {
226 return;
227 }
228 auto resourceObject = AceType::MakeRefPtr<ResourceObject>(bundleName, moduleName, context->GetInstanceId());
229 RefPtr<ResourceAdapter> resourceAdapter = nullptr;
230 RefPtr<ThemeConstants> themeConstants = nullptr;
231 if (SystemProperties::GetResourceDecoupling()) {
232 resourceAdapter = ResourceManager::GetInstance().GetOrCreateResourceAdapter(resourceObject);
233 CHECK_NULL_VOID(resourceAdapter);
234 } else {
235 auto themeManager = PipelineBase::CurrentThemeManager();
236 CHECK_NULL_VOID(themeManager);
237 themeConstants = themeManager->GetThemeConstants();
238 CHECK_NULL_VOID(themeConstants);
239 }
240 auto resourceWrapper = AceType::MakeRefPtr<ResourceWrapper>(themeConstants, resourceAdapter);
241 std::string rawFile;
242 std::unique_ptr<uint8_t[]> data;
243 size_t dataLen = 0;
244 std::smatch matches;
245 if (std::regex_match(fontLoader->familySrc_, matches, RAWFILE_APP_RES_PATH_REGEX) &&
246 matches.size() == RAWFILE_RESOURCE_MATCH_SIZE) {
247 rawFile = matches[1].str();
248 }
249 if (rawFile.empty()) {
250 TAG_LOGW(AceLogTag::ACE_FONT, "LoadFromResource rawFile is empty");
251 return;
252 }
253
254 if (!resourceWrapper->GetRawFileData(rawFile, dataLen, data, bundleName, moduleName) || !data.get()) {
255 TAG_LOGW(AceLogTag::ACE_FONT, "Get font data by rawFile failed, src:%{private}s, rawFile:%{public}s",
256 fontLoader->familySrc_.c_str(), rawFile.c_str());
257 return;
258 }
259
260 const std::vector<uint8_t> fontData(data.get(), data.get() + dataLen);
261 fontLoader->PostLoadFontTask(std::move(fontData), context);
262 },
263 TaskExecutor::TaskType::BACKGROUND, "ArkUIFontLoadFromResource");
264 }
265
LoadFromAsset(const RefPtr<PipelineBase> & context)266 void RosenFontLoader::LoadFromAsset(const RefPtr<PipelineBase>& context)
267 {
268 auto weakContext = AceType::WeakClaim(AceType::RawPtr(context));
269 context->GetTaskExecutor()->PostTask(
270 [weak = AceType::WeakClaim(this), weakContext] {
271 auto fontLoader = weak.Upgrade();
272 auto context = weakContext.Upgrade();
273 if (!fontLoader || !context) {
274 return;
275 }
276 auto assetManager = context->GetAssetManager();
277 if (!assetManager) {
278 return;
279 }
280 TAG_LOGI(AceLogTag::ACE_FONT, "begin to load font from asset.");
281 std::string assetSrc(fontLoader->familySrc_);
282 if (assetSrc[0] == '/') {
283 assetSrc = assetSrc.substr(1); // get the asset src without '/'.
284 } else if (assetSrc[0] == '.' && assetSrc.size() > 2 && assetSrc[1] == '/') {
285 assetSrc = assetSrc.substr(2); // get the asset src without './'.
286 }
287 auto assetData = assetManager->GetAsset(assetSrc);
288 if (!assetData) {
289 TAG_LOGW(AceLogTag::ACE_FONT, "No asset data!");
290 return;
291 }
292 const std::vector<uint8_t> fontData(assetData->GetData(), assetData->GetData() + assetData->GetSize());
293 fontLoader->PostLoadFontTask(std::move(fontData), context);
294 },
295 TaskExecutor::TaskType::BACKGROUND, "ArkUIFontLoadFromAsset");
296 }
297
PostLoadFontTask(const std::vector<uint8_t> & fontData,const RefPtr<PipelineBase> & context)298 void RosenFontLoader::PostLoadFontTask(const std::vector<uint8_t>& fontData, const RefPtr<PipelineBase>& context)
299 {
300 CHECK_NULL_VOID(context);
301 context->GetTaskExecutor()->PostTask(
302 [fontData, weak = AceType::WeakClaim(this)] {
303 auto fontLoader = weak.Upgrade();
304 CHECK_NULL_VOID(fontLoader);
305 // Load font.
306 RosenFontCollection::GetInstance().LoadFontFromList(
307 fontData.data(), fontData.size(), fontLoader->familyName_);
308 fontLoader->isLoaded_ = true;
309
310 // When font is already loaded, notify all which used this font.
311 fontLoader->NotifyCallbacks();
312 },
313 TaskExecutor::TaskType::UI, "ArkUIFontLoadFromList");
314 }
315
NotifyCallbacks()316 void RosenFontLoader::NotifyCallbacks()
317 {
318 for (const auto& [node, callback] : callbacks_) {
319 if (callback) {
320 callback();
321 }
322 }
323 callbacks_.clear();
324 std::stringstream ssNodes;
325 ssNodes << "[";
326 for (const auto& [node, callback] : callbacksNG_) {
327 auto uiNode = node.Upgrade();
328 if (uiNode) {
329 ssNodes << std::to_string(uiNode->GetId());
330 ssNodes << ", ";
331 }
332 if (callback) {
333 callback();
334 }
335 }
336 ssNodes << "]";
337 ACE_SCOPED_TRACE("Load Success! NotifyCallbacks [familyName:%s][size:%d][nodes:%s]", familyName_.c_str(),
338 static_cast<uint32_t>(callbacksNG_.size()), ssNodes.str().c_str());
339 TAG_LOGI(AceLogTag::ACE_FONT,
340 "Load Success! NotifyCallbacks [familyName:%{public}s][size:%{public}d][nodes:%{public}s]", familyName_.c_str(),
341 static_cast<uint32_t>(callbacksNG_.size()), ssNodes.str().c_str());
342 callbacksNG_.clear();
343 if (variationChanged_) {
344 variationChanged_();
345 }
346 }
347
348 } // namespace OHOS::Ace
349