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