• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 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 <fstream>
17 #include "js_fontcollection.h"
18 
19 #include "ability.h"
20 #include "napi_async_work.h"
21 
22 namespace OHOS::Rosen {
23 namespace {
24 constexpr size_t FILE_HEAD_LENGTH = 7; // 7 is the size of "file://"
25 const std::string CLASS_NAME = "FontCollection";
26 const int32_t GLOBAL_ERROR = 10000;
27 struct FontArgumentsConcreteContext : public ContextBase {
28     std::string familyName;
29     std::string filePath;
30     ResourceInfo info;
31 };
32 
ParseContextFilePath(napi_env env,napi_value * argv,sptr<FontArgumentsConcreteContext> context)33 bool ParseContextFilePath(napi_env env, napi_value* argv, sptr<FontArgumentsConcreteContext> context)
34 {
35     napi_valuetype valueType = napi_undefined;
36     napi_typeof(env, argv[ARGC_ONE], &valueType);
37 
38     if (valueType == napi_object) {
39         return false;
40     } else if (valueType == napi_string) {
41         if (!ConvertFromJsValue(env, argv[ARGC_ONE], context->filePath)) {
42             std::string errMessage("Failed to convert file path:");
43             errMessage += context->filePath;
44             context->status = napi_invalid_arg;
45             context->errMessage = errMessage;
46             (context)->errCode = static_cast<int32_t>(TextErrorCode::ERROR_INVALID_PARAM);
47             TEXT_LOGE("%{public}s", errMessage.c_str());
48         }
49     }
50     return true;
51 }
52 }
53 
54 std::mutex JsFontCollection::constructorMutex_;
55 thread_local napi_ref JsFontCollection::constructor_ = nullptr;
56 
Constructor(napi_env env,napi_callback_info info)57 napi_value JsFontCollection::Constructor(napi_env env, napi_callback_info info)
58 {
59     size_t argCount = 0;
60     napi_value jsThis = nullptr;
61     napi_status status = napi_get_cb_info(env, info, &argCount, nullptr, &jsThis, nullptr);
62     if (status != napi_ok) {
63         TEXT_LOGE("Failed to get params");
64         return nullptr;
65     }
66 
67     JsFontCollection* jsFontCollection = new(std::nothrow) JsFontCollection();
68     if (jsFontCollection == nullptr) {
69         TEXT_LOGE("Failed to new font collection");
70         return nullptr;
71     }
72     status = napi_wrap(env, jsThis, jsFontCollection,
73         JsFontCollection::Destructor, nullptr, nullptr);
74     if (status != napi_ok) {
75         delete jsFontCollection;
76         TEXT_LOGE("Failed to wrap font collection");
77         return nullptr;
78     }
79     return jsThis;
80 }
81 
Init(napi_env env,napi_value exportObj)82 napi_value JsFontCollection::Init(napi_env env, napi_value exportObj)
83 {
84     if (!CreateConstructor(env)) {
85         TEXT_LOGE("Failed to create constructor");
86         return nullptr;
87     }
88     napi_value constructor = nullptr;
89     napi_status status = napi_get_reference_value(env, constructor_, &constructor);
90     if (status != napi_ok) {
91         TEXT_LOGE("Failed to get reference, ret %{public}d", status);
92         return nullptr;
93     }
94 
95     status = napi_set_named_property(env, exportObj, CLASS_NAME.c_str(), constructor);
96     if (status != napi_ok) {
97         TEXT_LOGE("Failed to set named property");
98         return nullptr;
99     }
100     return exportObj;
101 }
102 
CreateConstructor(napi_env env)103 bool JsFontCollection::CreateConstructor(napi_env env)
104 {
105     std::lock_guard<std::mutex> lock(constructorMutex_);
106     if (constructor_) {
107         return true;
108     }
109     napi_property_descriptor properties[] = {
110         DECLARE_NAPI_STATIC_FUNCTION("getGlobalInstance", JsFontCollection::GetGlobalInstance),
111         DECLARE_NAPI_FUNCTION("loadFontSync", JsFontCollection::LoadFontSync),
112         DECLARE_NAPI_FUNCTION("clearCaches", JsFontCollection::ClearCaches),
113         DECLARE_NAPI_FUNCTION("loadFont", JsFontCollection::LoadFontAsync),
114         DECLARE_NAPI_FUNCTION("unloadFontSync", JsFontCollection::UnloadFontSync),
115         DECLARE_NAPI_FUNCTION("unloadFont", JsFontCollection::UnloadFontAsync),
116     };
117 
118     napi_value constructor = nullptr;
119     napi_status status = napi_define_class(env, CLASS_NAME.c_str(), NAPI_AUTO_LENGTH, Constructor, nullptr,
120         sizeof(properties) / sizeof(properties[0]), properties, &constructor);
121     if (status != napi_ok) {
122         TEXT_LOGE("Failed to define class, ret %{public}d", status);
123         return false;
124     }
125 
126     status = napi_create_reference(env, constructor, 1, &constructor_);
127     if (status != napi_ok) {
128         TEXT_LOGE("Failed to create reference, ret %{public}d", status);
129         return false;
130     }
131     return true;
132 }
133 
Destructor(napi_env env,void * nativeObject,void * finalize)134 void JsFontCollection::Destructor(napi_env env, void* nativeObject, void* finalize)
135 {
136     (void)finalize;
137     if (nativeObject != nullptr) {
138         JsFontCollection* napi = reinterpret_cast<JsFontCollection*>(nativeObject);
139         delete napi;
140     }
141 }
142 
JsFontCollection()143 JsFontCollection::JsFontCollection()
144 {
145     fontcollection_ = OHOS::Rosen::FontCollection::From(nullptr);
146 }
147 
GetFontCollection()148 std::shared_ptr<FontCollection> JsFontCollection::GetFontCollection()
149 {
150     return fontcollection_;
151 }
152 
GetGlobalInstance(napi_env env,napi_callback_info info)153 napi_value JsFontCollection::GetGlobalInstance(napi_env env, napi_callback_info info)
154 {
155     if (!CreateConstructor(env)) {
156         TEXT_LOGE("Failed to create constructor");
157         return nullptr;
158     }
159     napi_value constructor = nullptr;
160     napi_status status = napi_get_reference_value(env, constructor_, &constructor);
161     if (status != napi_ok || !constructor) {
162         TEXT_LOGE("Failed to get constructor object");
163         return nullptr;
164     }
165 
166     napi_value object = nullptr;
167     status = napi_new_instance(env, constructor, 0, nullptr, &object);
168     if (status != napi_ok || !object) {
169         TEXT_LOGE("Failed to new instance");
170         return nullptr;
171     }
172 
173     JsFontCollection* jsFontCollection = nullptr;
174     status = napi_unwrap(env, object, reinterpret_cast<void**>(&jsFontCollection));
175     if (status != napi_ok || !jsFontCollection) {
176         TEXT_LOGE("Failed to unwrap font collection");
177         return nullptr;
178     }
179     jsFontCollection->fontcollection_ = OHOS::Rosen::FontCollection::Create();
180 
181     return object;
182 }
183 
LoadFontSync(napi_env env,napi_callback_info info)184 napi_value JsFontCollection::LoadFontSync(napi_env env, napi_callback_info info)
185 {
186     JsFontCollection* me = CheckParamsAndGetThis<JsFontCollection>(env, info);
187     return (me != nullptr) ? me->OnLoadFont(env, info) : nullptr;
188 }
189 
SplitAbsoluteFontPath(std::string & absolutePath)190 bool JsFontCollection::SplitAbsoluteFontPath(std::string& absolutePath)
191 {
192     auto iter = absolutePath.find_first_of(':');
193     if (iter == std::string::npos) {
194         TEXT_LOGE("Failed to find separator in path:%{public}s", absolutePath.c_str());
195         return false;
196     }
197     std::string head = absolutePath.substr(0, iter);
198     if ((head == "file" && absolutePath.size() > FILE_HEAD_LENGTH)) {
199         absolutePath = absolutePath.substr(iter + 3); // 3 means skip "://"
200         // the file format is like "file://system/fonts...",
201         return true;
202     }
203 
204     return false;
205 }
206 
GetResourcePartData(napi_env env,ResourceInfo & info,napi_value paramsNApi,napi_value bundleNameNApi,napi_value moduleNameNApi)207 bool JsFontCollection::GetResourcePartData(napi_env env, ResourceInfo& info, napi_value paramsNApi,
208     napi_value bundleNameNApi, napi_value moduleNameNApi)
209 {
210     napi_valuetype valueType = napi_undefined;
211     bool isArray = false;
212     if (napi_is_array(env, paramsNApi, &isArray) != napi_ok) {
213         TEXT_LOGE("Failed to get array type");
214         return false;
215     }
216     if (!isArray) {
217         TEXT_LOGE("Invalid array type");
218         return false;
219     }
220 
221     uint32_t arrayLength = 0;
222     napi_get_array_length(env, paramsNApi, &arrayLength);
223     for (uint32_t i = 0; i < arrayLength; i++) {
224         size_t ret = 0;
225         napi_value indexValue = nullptr;
226         napi_get_element(env, paramsNApi, i, &indexValue);
227         napi_typeof(env, indexValue, &valueType);
228         if (valueType == napi_string) {
229             size_t strLen = GetParamLen(env, indexValue) + 1;
230             std::unique_ptr<char[]> indexStr = std::make_unique<char[]>(strLen);
231             napi_get_value_string_utf8(env, indexValue, indexStr.get(), strLen, &ret);
232             info.params.emplace_back(indexStr.get());
233         } else if (valueType == napi_number) {
234             int32_t num = 0;
235             napi_get_value_int32(env, indexValue, &num);
236             info.params.emplace_back(std::to_string(num));
237         } else {
238             TEXT_LOGE("Invalid value type %{public}d", valueType);
239             return false;
240         }
241     }
242 
243     napi_typeof(env, bundleNameNApi, &valueType);
244     if (valueType == napi_string) {
245         size_t ret = 0;
246         size_t strLen = GetParamLen(env, bundleNameNApi) + 1;
247         std::unique_ptr<char[]> bundleNameStr = std::make_unique<char[]>(strLen);
248         napi_get_value_string_utf8(env, bundleNameNApi, bundleNameStr.get(), strLen, &ret);
249         info.bundleName = bundleNameStr.get();
250     }
251 
252     napi_typeof(env, moduleNameNApi, &valueType);
253     if (valueType == napi_string) {
254         size_t ret = 0;
255         size_t strLen = GetParamLen(env, moduleNameNApi) + 1;
256         std::unique_ptr<char[]> moduleNameStr = std::make_unique<char[]>(strLen);
257         napi_get_value_string_utf8(env, moduleNameNApi, moduleNameStr.get(), strLen, &ret);
258         info.moduleName = moduleNameStr.get();
259     }
260 
261     return true;
262 }
263 
ParseResourceType(napi_env env,napi_value value,ResourceInfo & info)264 bool JsFontCollection::ParseResourceType(napi_env env, napi_value value, ResourceInfo& info)
265 {
266     napi_value idNApi = nullptr;
267     napi_value typeNApi = nullptr;
268     napi_value paramsNApi = nullptr;
269     napi_value bundleNameNApi = nullptr;
270     napi_value moduleNameNApi = nullptr;
271     napi_valuetype valueType = napi_undefined;
272     napi_typeof(env, value, &valueType);
273     if (valueType == napi_object) {
274         napi_get_named_property(env, value, "id", &idNApi);
275         napi_get_named_property(env, value, "type", &typeNApi);
276         napi_get_named_property(env, value, "params", &paramsNApi);
277         napi_get_named_property(env, value, "bundleName", &bundleNameNApi);
278         napi_get_named_property(env, value, "moduleName", &moduleNameNApi);
279     } else {
280         return false;
281     }
282 
283     napi_typeof(env, idNApi, &valueType);
284     if (valueType == napi_number) {
285         napi_get_value_int32(env, idNApi, &info.resId);
286     }
287 
288     napi_typeof(env, typeNApi, &valueType);
289     if (valueType == napi_number) {
290         napi_get_value_int32(env, typeNApi, &info.type);
291     }
292     if (!GetResourcePartData(env, info, paramsNApi, bundleNameNApi, moduleNameNApi)) {
293         return false;
294     }
295 
296     return true;
297 }
298 
GetResourceManager() const299 std::shared_ptr<Global::Resource::ResourceManager> JsFontCollection::GetResourceManager() const
300 {
301     std::shared_ptr<AbilityRuntime::ApplicationContext> context =
302         AbilityRuntime::ApplicationContext::GetApplicationContext();
303     TEXT_ERROR_CHECK(context != nullptr, return nullptr, "Failed to get application context");
304     auto resourceManager = context->GetResourceManager();
305     TEXT_ERROR_CHECK(resourceManager != nullptr, return nullptr, "Failed to get resource manager");
306     return resourceManager;
307 }
308 
ParseResourcePath(const std::string familyName,ResourceInfo & info)309 bool JsFontCollection::ParseResourcePath(const std::string familyName, ResourceInfo& info)
310 {
311     int32_t state = 0;
312     auto resourceManager = GetResourceManager();
313     TEXT_ERROR_CHECK(resourceManager != nullptr, return false,
314         "Failed to get resourceManager, resourceManager is nullptr");
315 
316     if (info.type == static_cast<int32_t>(ResourceType::STRING)) {
317         std::string rPath;
318         if (info.resId < 0 && !info.params.empty() && info.params[0].size() > 0) {
319             rPath = info.params[0];
320         } else {
321             state = resourceManager->GetStringById(info.resId, rPath);
322             if (state >= GLOBAL_ERROR || state < 0) {
323                 return false;
324             }
325             if (!SplitAbsoluteFontPath(rPath) || !GetFontFileProperties(rPath, familyName)) {
326                 return false;
327             }
328         }
329     } else if (info.type == static_cast<int32_t>(ResourceType::RAWFILE)) {
330         size_t dataLen = 0;
331         std::unique_ptr<uint8_t[]> rawData;
332 
333         if (info.params.empty()) {
334             return false;
335         }
336 
337         state = resourceManager->GetRawFileFromHap(info.params[0], dataLen, rawData);
338         if (state >= GLOBAL_ERROR || state < 0) {
339             return false;
340         }
341         if (!fontcollection_->LoadFont(familyName.c_str(), rawData.get(), dataLen)) {
342             return false;
343         }
344         return true;
345     } else {
346         TEXT_LOGE("Invalid resource type %{public}d", info.type);
347         return false;
348     }
349     return true;
350 }
351 
GetFontFileProperties(const std::string path,const std::string familyName)352 bool JsFontCollection::GetFontFileProperties(const std::string path, const std::string familyName)
353 {
354     if (fontcollection_ == nullptr) {
355         TEXT_LOGE("Null font collection");
356         return false;
357     }
358 
359     char tmpPath[PATH_MAX] = {0};
360     if (realpath(path.c_str(), tmpPath) == nullptr) {
361         TEXT_LOGE("Invalid path %{public}s", path.c_str());
362         return false;
363     }
364 
365     std::ifstream f(tmpPath);
366     if (!f.good()) {
367         TEXT_LOGE("Failed to access %{public}s, %{public}s", tmpPath, strerror(errno));
368         return false;
369     }
370 
371     std::ifstream ifs(tmpPath, std::ios_base::in);
372     if (!ifs.is_open()) {
373         return false;
374     }
375 
376     ifs.seekg(0, ifs.end);
377     if (!ifs.good()) {
378         ifs.close();
379         return false;
380     }
381 
382     size_t datalen = static_cast<size_t>(ifs.tellg());
383     if (ifs.fail()) {
384         ifs.close();
385         return false;
386     }
387 
388     ifs.seekg(ifs.beg);
389     if (!ifs.good()) {
390         ifs.close();
391         return false;
392     }
393 
394     std::unique_ptr<char[]> buffer = std::make_unique<char[]>(datalen);
395     ifs.read(buffer.get(), datalen);
396     if (!ifs.good()) {
397         TEXT_LOGE("Failed to read %{public}s, data len %{public}zu, %{public}s",
398             tmpPath, datalen, strerror(errno));
399         ifs.close();
400         return false;
401     }
402     ifs.close();
403     const uint8_t* rawData = reinterpret_cast<uint8_t*>(buffer.get());
404     if (!fontcollection_->LoadFont(familyName.c_str(), rawData, datalen)) {
405         TEXT_LOGE("Failed to load font %{public}s", familyName.c_str());
406         return false;
407     }
408     return true;
409 }
410 
OnLoadFont(napi_env env,napi_callback_info info)411 napi_value JsFontCollection::OnLoadFont(napi_env env, napi_callback_info info)
412 {
413     size_t argc = ARGC_TWO;
414     napi_value argv[ARGC_TWO] = {nullptr};
415     if (napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr) != napi_ok ||
416         argc < ARGC_TWO) {
417         TEXT_LOGE("Failed to get argument, argc %{public}zu", argc);
418         return nullptr;
419     }
420     if (argv[0] == nullptr) {
421         TEXT_LOGE("Null argv[0]");
422         return nullptr;
423     }
424     std::string familyName;
425     std::string familySrc;
426     if (!ConvertFromJsValue(env, argv[0], familyName)) {
427         TEXT_LOGE("Failed to convert argv[0]");
428         return nullptr;
429     }
430     napi_valuetype valueType = napi_undefined;
431     if (argv[1] == nullptr) {
432         TEXT_LOGE("Null arv[1]");
433         return nullptr;
434     }
435     napi_typeof(env, argv[1], &valueType);
436     if (valueType != napi_object) {
437         if (!ConvertFromJsValue(env, argv[1], familySrc)) {
438             TEXT_LOGE("Failed to convert argv[1]");
439             return nullptr;
440         }
441         if (!SplitAbsoluteFontPath(familySrc) || !GetFontFileProperties(familySrc, familyName)) {
442             return nullptr;
443         }
444         return NapiGetUndefined(env);
445     }
446 
447     ResourceInfo resourceInfo;
448     if (!ParseResourceType(env, argv[1], resourceInfo) || !ParseResourcePath(familyName, resourceInfo)) {
449         return nullptr;
450     }
451 
452     return NapiGetUndefined(env);
453 }
454 
ClearCaches(napi_env env,napi_callback_info info)455 napi_value JsFontCollection::ClearCaches(napi_env env, napi_callback_info info)
456 {
457     JsFontCollection* me = CheckParamsAndGetThis<JsFontCollection>(env, info);
458     return (me != nullptr) ? me->OnClearCaches(env, info) : nullptr;
459 }
460 
OnClearCaches(napi_env env,napi_callback_info info)461 napi_value JsFontCollection::OnClearCaches(napi_env env, napi_callback_info info)
462 {
463     if (fontcollection_ == nullptr) {
464         TEXT_LOGE("Null font collection");
465         return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM,
466             "JsFontCollection::OnClearCaches fontCollection is nullptr.");
467     }
468     fontcollection_->ClearCaches();
469     return NapiGetUndefined(env);
470 }
471 
LoadFontAsync(napi_env env,napi_callback_info info)472 napi_value JsFontCollection::LoadFontAsync(napi_env env, napi_callback_info info)
473 {
474     JsFontCollection* me = CheckParamsAndGetThis<JsFontCollection>(env, info);
475     return (me != nullptr) ? me->OnLoadFontAsync(env, info) : nullptr;
476 }
477 
OnLoadFontAsync(napi_env env,napi_callback_info info)478 napi_value JsFontCollection::OnLoadFontAsync(napi_env env, napi_callback_info info)
479 {
480     sptr<FontArgumentsConcreteContext> context = sptr<FontArgumentsConcreteContext>::MakeSptr();
481     NAPI_CHECK_AND_THROW_ERROR(context != nullptr, TextErrorCode::ERROR_NO_MEMORY, "Failed to make context");
482     auto inputParser = [env, context](size_t argc, napi_value* argv) {
483         TEXT_ERROR_CHECK(argv != nullptr, return, "Argv is null");
484         NAPI_CHECK_ARGS(context, context->status == napi_ok, napi_invalid_arg,
485             TextErrorCode::ERROR_INVALID_PARAM, return, "Status error, status=%d", static_cast<int>(context->status));
486         NAPI_CHECK_ARGS(context, argc >= ARGC_TWO, napi_invalid_arg, TextErrorCode::ERROR_INVALID_PARAM,
487             return, "Argc is invalid %zu", argc);
488         NAPI_CHECK_ARGS(context, ConvertFromJsValue(env, argv[0], context->familyName), napi_invalid_arg,
489             TextErrorCode::ERROR_INVALID_PARAM, return, "FamilyName is invalid %s", context->familyName.c_str());
490 
491         if (!ParseContextFilePath(env, argv, context)) {
492             auto* fontCollection = reinterpret_cast<JsFontCollection*>(context->native);
493             NAPI_CHECK_ARGS(context, fontCollection != nullptr, napi_invalid_arg,
494                 TextErrorCode::ERROR_INVALID_PARAM, return, "FontCollection is null");
495             NAPI_CHECK_ARGS(context, fontCollection->ParseResourceType(env, argv[ARGC_ONE], context->info),
496                 napi_invalid_arg, TextErrorCode::ERROR_INVALID_PARAM, return, "Parse resource error");
497         }
498     };
499 
500     context->GetCbInfo(env, info, inputParser);
501 
502     auto executor = [context]() {
503         TEXT_ERROR_CHECK(context != nullptr, return, "Context is null");
504 
505         auto* fontCollection = reinterpret_cast<JsFontCollection*>(context->native);
506         NAPI_CHECK_ARGS(context, fontCollection != nullptr, napi_generic_failure,
507             TextErrorCode::ERROR_INVALID_PARAM, return, "FontCollection is null");
508 
509         NAPI_CHECK_ARGS(context, fontCollection->fontcollection_ != nullptr, napi_generic_failure,
510             TextErrorCode::ERROR_INVALID_PARAM, return, "Inner fontcollection is null");
511 
512         if (!context->filePath.empty()) {
513             NAPI_CHECK_ARGS(context, fontCollection->SplitAbsoluteFontPath(context->filePath),
514                 napi_invalid_arg, TextErrorCode::ERROR_INVALID_PARAM, return, "Failed to split absolute font path");
515 
516             NAPI_CHECK_ARGS(context, fontCollection->GetFontFileProperties(context->filePath,
517                 context->familyName), napi_invalid_arg, TextErrorCode::ERROR_INVALID_PARAM, return,
518                 "Failed to get font file properties");
519         } else {
520             NAPI_CHECK_ARGS(context, fontCollection->ParseResourcePath(context->familyName, context->info),
521                 napi_invalid_arg, TextErrorCode::ERROR_INVALID_PARAM, return,
522                 "Failed to execute function, path is invalid");
523         }
524     };
525 
526     auto complete = [env](napi_value& output) {
527         output = NapiGetUndefined(env);
528     };
529     return NapiAsyncWork::Enqueue(env, context, "OnLoadFontAsync", executor, complete);
530 }
531 
UnloadFontAsync(napi_env env,napi_callback_info info)532 napi_value JsFontCollection::UnloadFontAsync(napi_env env, napi_callback_info info)
533 {
534     JsFontCollection* me = CheckParamsAndGetThis<JsFontCollection>(env, info);
535     return (me != nullptr) ? me->OnUnloadFontAsync(env, info) : nullptr;
536 }
537 
UnloadFontSync(napi_env env,napi_callback_info info)538 napi_value JsFontCollection::UnloadFontSync(napi_env env, napi_callback_info info)
539 {
540     JsFontCollection* me = CheckParamsAndGetThis<JsFontCollection>(env, info);
541     return (me != nullptr) ? me->OnUnloadFont(env, info) : nullptr;
542 }
543 
544 
OnUnloadFontAsync(napi_env env,napi_callback_info info)545 napi_value JsFontCollection::OnUnloadFontAsync(napi_env env, napi_callback_info info)
546 {
547     sptr<FontArgumentsConcreteContext> context = sptr<FontArgumentsConcreteContext>::MakeSptr();
548     NAPI_CHECK_AND_THROW_ERROR(context != nullptr, TextErrorCode::ERROR_NO_MEMORY, "Failed to make context");
549     auto inputParser = [env, context](size_t argc, napi_value* argv) {
550         TEXT_ERROR_CHECK(argv != nullptr, return, "Argv is null");
551         NAPI_CHECK_ARGS(context, context->status == napi_ok, napi_invalid_arg,
552             TextErrorCode::ERROR_INVALID_PARAM, return, "Status error, status=%d", static_cast<int>(context->status));
553         NAPI_CHECK_ARGS(context, argc >= ARGC_ONE, napi_invalid_arg, TextErrorCode::ERROR_INVALID_PARAM,
554             return, "Argc is invalid %zu", argc);
555         NAPI_CHECK_ARGS(context, ConvertFromJsValue(env, argv[0], context->familyName), napi_invalid_arg,
556             TextErrorCode::ERROR_INVALID_PARAM, return, "FamilyName is invalid %s", context->familyName.c_str());
557     };
558 
559     context->GetCbInfo(env, info, inputParser);
560 
561     auto executor = [context]() {
562         TEXT_ERROR_CHECK(context != nullptr, return, "Context is null");
563 
564         auto* fontCollection = reinterpret_cast<JsFontCollection*>(context->native);
565         NAPI_CHECK_ARGS(context, fontCollection != nullptr, napi_generic_failure, TextErrorCode::ERROR_INVALID_PARAM,
566             return, "FontCollection is null");
567 
568         NAPI_CHECK_ARGS(context, fontCollection->fontcollection_ != nullptr, napi_generic_failure,
569             TextErrorCode::ERROR_INVALID_PARAM, return, "Inner fontcollection is null");
570         fontCollection->fontcollection_->UnloadFont(context->familyName);
571     };
572 
573     auto complete = [env](napi_value& output) {
574         output = NapiGetUndefined(env);
575     };
576     return NapiAsyncWork::Enqueue(env, context, "OnUnloadFontAsync", executor, complete);
577 }
578 
OnUnloadFont(napi_env env,napi_callback_info info)579 napi_value JsFontCollection::OnUnloadFont(napi_env env, napi_callback_info info)
580 {
581     size_t argc = ARGC_ONE;
582     napi_value argv[ARGC_ONE] = { nullptr };
583     if (napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr) != napi_ok || argc < ARGC_ONE) {
584         TEXT_LOGE("Failed to get argument, argc %{public}zu", argc);
585         return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Failed to get argument");
586     }
587     if (argv[0] == nullptr) {
588         TEXT_LOGE("Null argv[0]");
589         return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Failed to get argument");
590     }
591     std::string familyName;
592     if (!ConvertFromJsValue(env, argv[0], familyName)) {
593         TEXT_LOGE("Failed to convert argv[0]");
594         return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Failed to get argument");
595     }
596 
597     fontcollection_->UnloadFont(familyName);
598 
599     return NapiGetUndefined(env);
600 }
601 
602 } // namespace OHOS::Rosen
603