• 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, 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