1/*
2 * Copyright (c) 2022 Shenzhen Kaihong Digital Industry Development 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
17#include "interfaces/napi/kits/utils/napi_utils.h"
18
19#include "bridge/common/utils/engine_helper.h"
20#include "core/common/font_manager.h"
21
22namespace OHOS::Ace::Napi {
23namespace {
24constexpr size_t STR_BUFFER_SIZE = 1024;
25constexpr int32_t FONT_INFO_INDEX_PATH = 0;
26constexpr int32_t FONT_INFO_INDEX_POST_SCRIPT_NAME = 1;
27constexpr int32_t FONT_INFO_INDEX_FULL_NAME = 2;
28constexpr int32_t FONT_INFO_INDEX_FAMILY = 3;
29constexpr int32_t FONT_INFO_INDEX_SUB_FAMILY = 4;
30constexpr int32_t FONT_INFO_INDEX_WEIGHT = 5;
31constexpr int32_t FONT_INFO_INDEX_WIDTH = 6;
32constexpr int32_t FONT_INFO_INDEX_ITALIC = 7;
33constexpr int32_t FONT_INFO_INDEX_MONOSPACE = 8;
34constexpr int32_t FONT_INFO_INDEX_SYMBOLIC = 9;
35constexpr int32_t FONT_INFO_INDEX_MAX = 10;
36}
37
38static bool ParseFamilyNameOrSrc(napi_env env, napi_value familyNameOrSrcNApi, std::string& familyNameOrSrc,
39    napi_valuetype valueType, ResourceInfo& info)
40{
41    napi_typeof(env, familyNameOrSrcNApi, &valueType);
42    if (valueType == napi_string) {
43        size_t nameLen = 0;
44        napi_get_value_string_utf8(env, familyNameOrSrcNApi, nullptr, 0, &nameLen);
45        std::unique_ptr<char[]> name = std::make_unique<char[]>(nameLen + 1);
46        napi_get_value_string_utf8(env, familyNameOrSrcNApi, name.get(), nameLen + 1, &nameLen);
47        familyNameOrSrc = name.get();
48    } else if (valueType == napi_object) {
49        if (!ParseResourceParam(env, familyNameOrSrcNApi, info)) {
50            return false;
51        }
52        if (!ParseString(info, familyNameOrSrc)) {
53            return false;
54        }
55    } else {
56        return false;
57    }
58    return true;
59}
60
61static napi_value JSRegisterFont(napi_env env, napi_callback_info info)
62{
63    size_t argc = 1;
64    napi_value argv = nullptr;
65    napi_value thisVar = nullptr;
66    void* data = nullptr;
67    napi_get_cb_info(env, info, &argc, &argv, &thisVar, &data);
68
69    napi_value familyNameNApi = nullptr;
70    napi_value familySrcNApi = nullptr;
71    std::string familyName;
72    std::string familySrc;
73
74    napi_valuetype valueType = napi_undefined;
75    napi_typeof(env, argv, &valueType);
76    if (valueType == napi_object) {
77        napi_get_named_property(env, argv, "familyName", &familyNameNApi);
78        napi_get_named_property(env, argv, "familySrc", &familySrcNApi);
79    } else {
80        return nullptr;
81    }
82
83    ResourceInfo resourceInfo;
84    if (!ParseFamilyNameOrSrc(env, familyNameNApi, familyName, valueType, resourceInfo)) {
85        return nullptr;
86    }
87    if (!ParseFamilyNameOrSrc(env, familySrcNApi, familySrc, valueType, resourceInfo)) {
88        return nullptr;
89    }
90
91    std::string bundleName = resourceInfo.bundleName.has_value() ? resourceInfo.bundleName.value() : "";
92    std::string moduleName = resourceInfo.moduleName.has_value() ? resourceInfo.moduleName.value() : "";
93    auto container = Container::CurrentSafely();
94    if (bundleName.empty() && container) {
95        bundleName = container->GetBundleName();
96    }
97    if (moduleName.empty() && container) {
98        moduleName = container->GetModuleName();
99    }
100    auto delegate = EngineHelper::GetCurrentDelegateSafely();
101    if (!delegate) {
102        return nullptr;
103    }
104    TAG_LOGI(AceLogTag::ACE_FONT, "begin to register font.");
105    delegate->RegisterFont(familyName, familySrc, bundleName, moduleName);
106    return nullptr;
107}
108
109static napi_value JSgetSystemFontList(napi_env env, napi_callback_info info)
110{
111    napi_value arrayResult = nullptr;
112    napi_create_array(env, &arrayResult);
113    bool isArray = false;
114    if (napi_is_array(env, arrayResult, &isArray) != napi_ok || !isArray) {
115        return arrayResult;
116    }
117    std::vector<std::string> fontList;
118    auto delegate = EngineHelper::GetCurrentDelegateSafely();
119    if (!delegate) {
120        return nullptr;
121    }
122    delegate->GetSystemFontList(fontList);
123
124    int32_t index = 0;
125    for (const std::string& font : fontList) {
126        napi_value result = nullptr;
127        napi_create_string_utf8(env, font.c_str(), font.length(), &result);
128        napi_set_element(env, arrayResult, index++, result);
129    }
130    return arrayResult;
131}
132
133static napi_value JSgetFontByName(napi_env env, napi_callback_info info)
134{
135    size_t argc = 1;
136    napi_value argv = nullptr;
137    napi_value thisVar = nullptr;
138    void* data = nullptr;
139    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &argv, &thisVar, &data));
140    NAPI_ASSERT(env, argc == 1, "requires 1 parameter");
141
142    napi_valuetype type;
143    NAPI_CALL(env, napi_typeof(env, argv, &type));
144    NAPI_ASSERT(env, type == napi_string, "type mismatch");
145    char fontName[STR_BUFFER_SIZE] = { 0 };
146    size_t len = 0;
147    napi_get_value_string_utf8(env, argv, fontName, STR_BUFFER_SIZE, &len);
148    NAPI_ASSERT(env, len < STR_BUFFER_SIZE, "condition string too long");
149    std::string fontNameStr(fontName, len);
150
151    FontInfo fontInfo;
152    auto delegate = EngineHelper::GetCurrentDelegateSafely();
153    if (!delegate) {
154        return nullptr;
155    }
156    if (!delegate->GetSystemFont(fontNameStr, fontInfo)) {
157        return nullptr;
158    }
159
160    napi_value resultArray[FONT_INFO_INDEX_MAX] = { 0 };
161    napi_create_string_utf8(env, fontInfo.path.c_str(), NAPI_AUTO_LENGTH, &resultArray[FONT_INFO_INDEX_PATH]);
162    napi_create_string_utf8(env, fontInfo.postScriptName.c_str(), NAPI_AUTO_LENGTH,
163        &resultArray[FONT_INFO_INDEX_POST_SCRIPT_NAME]);
164    napi_create_string_utf8(env, fontInfo.fullName.c_str(), NAPI_AUTO_LENGTH, &resultArray[FONT_INFO_INDEX_FULL_NAME]);
165    napi_create_string_utf8(env, fontInfo.family.c_str(), NAPI_AUTO_LENGTH, &resultArray[FONT_INFO_INDEX_FAMILY]);
166    napi_create_string_utf8(env, fontInfo.subfamily.c_str(), NAPI_AUTO_LENGTH,
167        &resultArray[FONT_INFO_INDEX_SUB_FAMILY]);
168    napi_create_int32(env, fontInfo.weight, &resultArray[FONT_INFO_INDEX_WEIGHT]);
169    napi_create_int32(env, fontInfo.width, &resultArray[FONT_INFO_INDEX_WIDTH]);
170    napi_get_boolean(env, fontInfo.italic, &resultArray[FONT_INFO_INDEX_ITALIC]);
171    napi_get_boolean(env, fontInfo.monoSpace, &resultArray[FONT_INFO_INDEX_MONOSPACE]);
172    napi_get_boolean(env, fontInfo.symbolic, &resultArray[FONT_INFO_INDEX_SYMBOLIC]);
173
174    napi_value result = nullptr;
175    napi_create_object(env, &result);
176    napi_set_named_property(env, result, "path", resultArray[FONT_INFO_INDEX_PATH]);
177    napi_set_named_property(env, result, "postScriptName", resultArray[FONT_INFO_INDEX_POST_SCRIPT_NAME]);
178    napi_set_named_property(env, result, "fullName", resultArray[FONT_INFO_INDEX_FULL_NAME]);
179    napi_set_named_property(env, result, "family", resultArray[FONT_INFO_INDEX_FAMILY]);
180    napi_set_named_property(env, result, "subfamily", resultArray[FONT_INFO_INDEX_SUB_FAMILY]);
181    napi_set_named_property(env, result, "weight", resultArray[FONT_INFO_INDEX_WEIGHT]);
182    napi_set_named_property(env, result, "width", resultArray[FONT_INFO_INDEX_WIDTH]);
183    napi_set_named_property(env, result, "italic", resultArray[FONT_INFO_INDEX_ITALIC]);
184    napi_set_named_property(env, result, "monoSpace", resultArray[FONT_INFO_INDEX_MONOSPACE]);
185    napi_set_named_property(env, result, "symbolic", resultArray[FONT_INFO_INDEX_SYMBOLIC]);
186
187    return result;
188}
189
190static napi_value GetUIFontGenericInfo(napi_env env, const FontConfigJsonInfo& fontConfigJsonInfo)
191{
192    napi_value genericSetResult = nullptr;
193    napi_create_array(env, &genericSetResult);
194    int32_t index = 0;
195    for (const FontGenericInfo& generic: fontConfigJsonInfo.genericSet) {
196        napi_value genericResult = nullptr;
197        napi_create_object(env, &genericResult);
198        napi_value familyResult = nullptr;
199        napi_create_string_utf8(env, generic.familyName.c_str(), generic.familyName.length(), &familyResult);
200        napi_value aliasSetResult = nullptr;
201        napi_create_array(env, &aliasSetResult);
202        int32_t index2 = 0;
203        for (const AliasInfo& alias: generic.aliasSet) {
204            napi_value aliasResult = nullptr;
205            napi_create_object(env, &aliasResult);
206            napi_value familyNameResult = nullptr;
207            napi_create_string_utf8(env, alias.familyName.c_str(), alias.familyName.length(), &familyNameResult);
208            napi_value weightResult = nullptr;
209            napi_create_int32(env, alias.weight, &weightResult);
210            napi_set_named_property(env, aliasResult, "name", familyNameResult);
211            napi_set_named_property(env, aliasResult, "weight", weightResult);
212            napi_set_element(env, aliasSetResult, index2++, aliasResult);
213        }
214        index2 = 0;
215        napi_value adjustSetResult = nullptr;
216        napi_create_array(env, &adjustSetResult);
217        for (const AdjustInfo& adjust: generic.adjustSet) {
218            napi_value adjustResult = nullptr;
219            napi_create_object(env, &adjustResult);
220            napi_value weightResult = nullptr;
221            napi_create_int32(env, adjust.origValue, &weightResult);
222            napi_value toResult = nullptr;
223            napi_create_int32(env, adjust.newValue, &toResult);
224            napi_set_named_property(env, adjustResult, "weight", weightResult);
225            napi_set_named_property(env, adjustResult, "to", toResult);
226            napi_set_element(env, adjustSetResult, index2++, adjustResult);
227        }
228        napi_set_named_property(env, genericResult, "family", familyResult);
229        napi_set_named_property(env, genericResult, "alias", aliasSetResult);
230        napi_set_named_property(env, genericResult, "adjust", adjustSetResult);
231        napi_set_element(env, genericSetResult, index++, genericResult);
232    }
233    return genericSetResult;
234}
235
236static napi_value GetUIFontFallbackInfo(napi_env env, const FontConfigJsonInfo& fontConfigJsonInfo)
237{
238    napi_value fallbackGroupSetResult = nullptr;
239    napi_create_array(env, &fallbackGroupSetResult);
240    int32_t index = 0;
241    for (const FallbackGroup& fallbackGroup: fontConfigJsonInfo.fallbackGroupSet) {
242        napi_value fallbackGroupResult = nullptr;
243        napi_create_object(env, &fallbackGroupResult);
244        napi_value fontSetNameResult = nullptr;
245        napi_create_string_utf8(env, fallbackGroup.groupName.c_str(),
246            fallbackGroup.groupName.length(), &fontSetNameResult);
247        napi_value fallbackListResult = nullptr;
248        napi_create_array(env, &fallbackListResult);
249        int32_t index2 = 0;
250        for (const FallbackInfo& fallback: fallbackGroup.fallbackInfoSet) {
251            napi_value fallbackResult = nullptr;
252            napi_create_object(env, &fallbackResult);
253            napi_value familyResult = nullptr;
254            napi_create_string_utf8(env, fallback.familyName.c_str(), fallback.familyName.length(), &familyResult);
255            napi_value languageResult = nullptr;
256            napi_create_string_utf8(env, fallback.font.c_str(), fallback.font.length(), &languageResult);
257
258            napi_set_named_property(env, fallbackResult, "language", languageResult);
259            napi_set_named_property(env, fallbackResult, "family", familyResult);
260            napi_set_element(env, fallbackListResult, index2++, fallbackResult);
261        }
262        napi_set_named_property(env, fallbackGroupResult, "fontSetName", fontSetNameResult);
263        napi_set_named_property(env, fallbackGroupResult, "fallback", fallbackListResult);
264        napi_set_element(env, fallbackGroupSetResult, index++, fallbackGroupResult);
265    }
266    return fallbackGroupSetResult;
267}
268
269static napi_value JsGetUIFontConfig(napi_env env, napi_callback_info info)
270{
271    FontConfigJsonInfo fontConfigJsonInfo;
272    auto delegate = EngineHelper::GetCurrentDelegateSafely();
273    if (!delegate) {
274        return nullptr;
275    }
276    delegate->GetUIFontConfig(fontConfigJsonInfo);
277    napi_value result = nullptr;
278    napi_create_object(env, &result);
279    napi_value fontDirSetResult = nullptr;
280    napi_create_array(env, &fontDirSetResult);
281    int32_t index = 0;
282    for (const std::string& fontDir : fontConfigJsonInfo.fontDirSet) {
283        napi_value fontDirResult = nullptr;
284        napi_create_string_utf8(env, fontDir.c_str(), fontDir.length(), &fontDirResult);
285        napi_set_element(env, fontDirSetResult, index++, fontDirResult);
286    }
287    napi_value genericSetResult = GetUIFontGenericInfo(env, fontConfigJsonInfo);
288    napi_value fallbackGroupSetResult = GetUIFontFallbackInfo(env, fontConfigJsonInfo);
289
290    napi_set_named_property(env, result, "fontDir", fontDirSetResult);
291    napi_set_named_property(env, result, "generic", genericSetResult);
292    napi_set_named_property(env, result, "fallbackGroups", fallbackGroupSetResult);
293    return result;
294}
295
296static napi_value FontExport(napi_env env, napi_value exports)
297{
298    napi_property_descriptor fontDesc[] = {
299        DECLARE_NAPI_FUNCTION("registerFont", JSRegisterFont),
300        DECLARE_NAPI_FUNCTION("getSystemFontList", JSgetSystemFontList),
301        DECLARE_NAPI_FUNCTION("getFontByName", JSgetFontByName),
302        DECLARE_NAPI_FUNCTION("getUIFontConfig", JsGetUIFontConfig)
303    };
304    NAPI_CALL(env, napi_define_properties(env, exports, sizeof(fontDesc) / sizeof(fontDesc[0]), fontDesc));
305    return exports;
306}
307
308static napi_module fontModule = {
309    .nm_version = 1,
310    .nm_flags = 0,
311    .nm_filename = nullptr,
312    .nm_register_func = FontExport,
313    .nm_modname = "font",
314    .nm_priv = ((void*)0),
315    .reserved = { 0 },
316};
317
318extern "C" __attribute__((constructor)) void FontRegister()
319{
320    napi_module_register(&fontModule);
321}
322} // namespace OHOS::Ace::Napi
323