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