• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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