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 #ifndef OHOS_JS_TEXT_UTILS_H
17 #define OHOS_JS_TEXT_UTILS_H
18 
19 #include <codecvt>
20 #include <map>
21 
22 #include "draw/color.h"
23 #include "native_engine/native_engine.h"
24 #include "native_engine/native_value.h"
25 #include "text_style.h"
26 #include "typography.h"
27 #include "typography_create.h"
28 #include "typography_style.h"
29 #include "utils/point.h"
30 #include "utils/text_log.h"
31 
32 namespace OHOS::Rosen {
33 constexpr size_t ARGC_ONE = 1;
34 constexpr size_t ARGC_TWO = 2;
35 constexpr size_t ARGC_THREE = 3;
36 constexpr size_t ARGC_FOUR = 4;
37 constexpr size_t ARGC_FIVE = 5;
38 constexpr size_t ARGC_SIX = 6;
39 
40 struct ResourceInfo {
41     int32_t resId = 0;
42     int32_t type = 0;
43     std::vector<std::string> params;
44     std::string bundleName;
45     std::string moduleName;
46 };
47 
48 enum class ResourceType {
49     COLOR = 10001,
50     FLOAT,
51     STRING,
52     PLURAL,
53     BOOLEAN,
54     INTARRAY,
55     INTEGER,
56     PATTERN,
57     STRARRAY,
58     MEDIA = 20000,
59     RAWFILE = 30000
60 };
61 
62 enum class TextErrorCode : int32_t {
63     ERROR = -1,
64     OK = 0,
65     ERROR_NO_PERMISSION = 201, // the value do not change. It is defined on all system
66     ERROR_INVALID_PARAM = 401, // the value do not change. It is defined on all system
67     ERROR_DEVICE_NOT_SUPPORT = 801, // the value do not change. It is defined on all system
68     ERROR_ABNORMAL_PARAM_VALUE = 18600001, // the value do not change. It is defined on color manager system
69     ERROR_NO_MEMORY = 8800100, // no memory
70 };
71 
72 #define GET_UNWRAP_PARAM(argc, value)                                                                                  \
73     do {                                                                                                               \
74         if ((napi_unwrap(env, argv[argc], reinterpret_cast<void**>(&value)) != napi_ok) || value == nullptr) {         \
75             return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM,                                          \
76                 std::string("Incorrect ") + __FUNCTION__ + " parameter" + std::to_string(argc) + " type.");            \
77         }                                                                                                              \
78     } while (0)
79 
80 
81 #define NAPI_CHECK_AND_THROW_ERROR(ret, errorCode, errorMessage)   \
82     do {                                                           \
83         if (!(ret)) {                                              \
84             TEXT_LOGE("%{public}s", #errorMessage);                \
85             return NapiThrowError(env, errorCode, errorMessage);   \
86         }                                                          \
87     } while (0)
88 
89 #define NAPI_CHECK_AND_CLOSE_SCOPE(env, statement, scope, ret)     \
90     do {                                                           \
91         if ((statement) != napi_ok) {                              \
92             TEXT_LOGE("%{public}s failed", #statement);            \
93             napi_close_handle_scope(env, scope);                   \
94             return ret;                                            \
95         }                                                          \
96     } while (0)
97 
98 template<class T>
99 T* CheckParamsAndGetThis(const napi_env env, napi_callback_info info, const char* name = nullptr)
100 {
101     if (env == nullptr || info == nullptr) {
102         return nullptr;
103     }
104     napi_value object = nullptr;
105     napi_value propertyNameValue = nullptr;
106     napi_value pointerValue = nullptr;
107     napi_get_cb_info(env, info, nullptr, nullptr, &object, nullptr);
108     if (object != nullptr && name != nullptr) {
109         napi_create_string_utf8(env, name, NAPI_AUTO_LENGTH, &propertyNameValue);
110     }
111     napi_value& resObject = propertyNameValue ? propertyNameValue : object;
112     if (resObject) {
113         return napi_unwrap(env, resObject, (void **)(&pointerValue)) == napi_ok ?
114             reinterpret_cast<T*>(pointerValue) : nullptr;
115     }
116     return nullptr;
117 }
118 
119 template<typename T, size_t N>
ArraySize(T (&)[N])120 inline constexpr size_t ArraySize(T (&)[N]) noexcept
121 {
122     return N;
123 }
124 
CreateJsUndefined(napi_env env)125 inline napi_value CreateJsUndefined(napi_env env)
126 {
127     napi_value result = nullptr;
128     napi_get_undefined(env, &result);
129     return result;
130 }
131 
CreateJsNull(napi_env env)132 inline napi_value CreateJsNull(napi_env env)
133 {
134     napi_value result = nullptr;
135     napi_get_null(env, &result);
136     return result;
137 }
138 
CreateJsNumber(napi_env env,int32_t value)139 inline napi_value CreateJsNumber(napi_env env, int32_t value)
140 {
141     napi_value result = nullptr;
142     napi_create_int32(env, value, &result);
143     return result;
144 }
145 
CreateJsNumber(napi_env env,uint32_t value)146 inline napi_value CreateJsNumber(napi_env env, uint32_t value)
147 {
148     napi_value result = nullptr;
149     napi_create_uint32(env, value, &result);
150     return result;
151 }
152 
CreateJsNumber(napi_env env,int64_t value)153 inline napi_value CreateJsNumber(napi_env env, int64_t value)
154 {
155     napi_value result = nullptr;
156     napi_create_int64(env, value, &result);
157     return result;
158 }
159 
CreateJsNumber(napi_env env,uint64_t value)160 inline napi_value CreateJsNumber(napi_env env, uint64_t value)
161 {
162     napi_value result = nullptr;
163     napi_create_int64(env, value, &result);
164     return result;
165 }
166 
CreateJsNumber(napi_env env,double value)167 inline napi_value CreateJsNumber(napi_env env, double value)
168 {
169     napi_value result = nullptr;
170     napi_create_double(env, value, &result);
171     return result;
172 }
173 
174 template<class T>
CreateJsValue(napi_env env,const T & value)175 napi_value CreateJsValue(napi_env env, const T& value)
176 {
177     using ValueType = std::remove_cv_t<std::remove_reference_t<T>>;
178     napi_value result = nullptr;
179     if constexpr (std::is_same_v<ValueType, bool>) {
180         napi_get_boolean(env, value, &result);
181         return result;
182     } else if constexpr (std::is_arithmetic_v<ValueType>) {
183         return CreateJsNumber(env, value);
184     } else if constexpr (std::is_same_v<ValueType, std::string>) {
185         napi_create_string_utf8(env, value.c_str(), value.length(), &result);
186         return result;
187     } else if constexpr (std::is_enum_v<ValueType>) {
188         return CreateJsNumber(env, static_cast<std::make_signed_t<ValueType>>(value));
189     } else if constexpr (std::is_same_v<ValueType, const char*>) {
190         (value != nullptr) ? napi_create_string_utf8(env, value, strlen(value), &result) :
191             napi_get_undefined(env, &result);
192         return result;
193     } else {
194         return result;
195     }
196 }
197 
ConvertFromJsNumber(napi_env env,napi_value jsValue,int32_t & value)198 inline bool ConvertFromJsNumber(napi_env env, napi_value jsValue, int32_t& value)
199 {
200     return napi_get_value_int32(env, jsValue, &value) == napi_ok;
201 }
202 
ConvertFromJsNumber(napi_env env,napi_value jsValue,uint32_t & value)203 inline bool ConvertFromJsNumber(napi_env env, napi_value jsValue, uint32_t& value)
204 {
205     return napi_get_value_uint32(env, jsValue, &value) == napi_ok;
206 }
207 
ConvertFromJsNumber(napi_env env,napi_value jsValue,int64_t & value)208 inline bool ConvertFromJsNumber(napi_env env, napi_value jsValue, int64_t& value)
209 {
210     return napi_get_value_int64(env, jsValue, &value) == napi_ok;
211 }
212 
ConvertFromJsNumber(napi_env env,napi_value jsValue,uint64_t & value)213 inline bool ConvertFromJsNumber(napi_env env, napi_value jsValue, uint64_t& value)
214 {
215     int64_t num;
216     auto res = napi_get_value_int64(env, jsValue, &num);
217     if (res == napi_ok) {
218         value = static_cast<uint64_t>(num);
219     }
220     return res == napi_ok;
221 }
222 
ConvertFromJsNumber(napi_env env,napi_value jsValue,double & value)223 inline bool ConvertFromJsNumber(napi_env env, napi_value jsValue, double& value)
224 {
225     return napi_get_value_double(env, jsValue, &value) == napi_ok;
226 }
227 
ConvertFromJsNumber(napi_env env,napi_value jsValue,bool & value)228 inline bool ConvertFromJsNumber(napi_env env, napi_value jsValue, bool& value)
229 {
230     return napi_get_value_bool(env, jsValue, &value) == napi_ok;
231 }
232 
233 template<class T>
ConvertFromJsValue(napi_env env,napi_value jsValue,T & value)234 bool ConvertFromJsValue(napi_env env, napi_value jsValue, T& value)
235 {
236     if (jsValue == nullptr) {
237         return false;
238     }
239 
240     using ValueType = std::remove_cv_t<std::remove_reference_t<T>>;
241     if constexpr (std::is_same_v<ValueType, bool>) {
242         return napi_get_value_bool(env, jsValue, &value) == napi_ok;
243     } else if constexpr (std::is_arithmetic_v<ValueType>) {
244         return ConvertFromJsNumber(env, jsValue, value);
245     } else if constexpr (std::is_same_v<ValueType, std::string>) {
246         size_t len = 0;
247         if (napi_get_value_string_utf8(env, jsValue, nullptr, 0, &len) != napi_ok) {
248             return false;
249         }
250         auto buffer = std::make_unique<char[]>(len + 1);
251         size_t strLength = 0;
252         if (napi_get_value_string_utf8(env, jsValue, buffer.get(), len + 1, &strLength) == napi_ok) {
253             value = buffer.get();
254             return true;
255         }
256         return false;
257     } else if constexpr (std::is_enum_v<ValueType>) {
258         std::make_signed_t<ValueType> numberValue = 0;
259         if (!ConvertFromJsNumber(env, jsValue, numberValue)) {
260             return false;
261         }
262         value = static_cast<ValueType>(numberValue);
263         return true;
264     }
265     return false;
266 }
267 
ConvertClampFromJsValue(napi_env env,napi_value jsValue,int32_t & value,int32_t lo,int32_t hi)268 inline bool ConvertClampFromJsValue(napi_env env, napi_value jsValue, int32_t& value, int32_t lo, int32_t hi)
269 {
270     if (jsValue == nullptr) {
271         return false;
272     }
273     bool ret = napi_get_value_int32(env, jsValue, &value) == napi_ok;
274     value = std::clamp(value, lo, hi);
275     return ret;
276 }
277 
GetDoubleAndConvertToJsValue(napi_env env,double d)278 inline napi_value GetDoubleAndConvertToJsValue(napi_env env, double d)
279 {
280     napi_value value = nullptr;
281     (void)napi_create_double(env, d, &value);
282     return value;
283 }
284 
GetStringAndConvertToJsValue(napi_env env,std::string str)285 inline napi_value GetStringAndConvertToJsValue(napi_env env, std::string str)
286 {
287     napi_value objValue = nullptr;
288     napi_create_string_utf8(env, str.c_str(), str.length(), &objValue);
289     return objValue;
290 }
291 
NapiGetUndefined(napi_env env)292 inline napi_value NapiGetUndefined(napi_env env)
293 {
294     napi_value result = nullptr;
295     napi_get_undefined(env, &result);
296     return result;
297 }
298 
GetPointAndConvertToJsValue(napi_env env,Drawing::Point & point)299 inline napi_value GetPointAndConvertToJsValue(napi_env env, Drawing::Point& point)
300 {
301     napi_value objValue = nullptr;
302     napi_create_object(env, &objValue);
303     if (objValue != nullptr) {
304         napi_set_named_property(env, objValue, "x", CreateJsNumber(env, point.GetX()));
305         napi_set_named_property(env, objValue, "y", CreateJsNumber(env, point.GetY()));
306     }
307     return objValue;
308 }
309 
GetPointXFromJsNumber(napi_env env,napi_value argValue,Drawing::Point & point)310 inline void GetPointXFromJsNumber(napi_env env, napi_value argValue, Drawing::Point& point)
311 {
312     napi_value objValue = nullptr;
313     double targetX = 0;
314     if (napi_get_named_property(env, argValue, "x", &objValue) != napi_ok ||
315         napi_get_value_double(env, objValue, &targetX) != napi_ok) {
316         TEXT_LOGE("The Parameter of number x about JsPoint is unvaild");
317         return;
318     }
319     point.SetX(targetX);
320     return;
321 }
322 
GetPointYFromJsNumber(napi_env env,napi_value argValue,Drawing::Point & point)323 inline void GetPointYFromJsNumber(napi_env env, napi_value argValue, Drawing::Point& point)
324 {
325     napi_value objValue = nullptr;
326     double targetY = 0;
327     if (napi_get_named_property(env, argValue, "y", &objValue) != napi_ok ||
328         napi_get_value_double(env, objValue, &targetY) != napi_ok) {
329         TEXT_LOGE("The Parameter of number y about JsPoint is unvaild");
330         return;
331     }
332     point.SetY(targetY);
333     return;
334 }
335 
GetPointFromJsValue(napi_env env,napi_value argValue,Drawing::Point & point)336 inline void GetPointFromJsValue(napi_env env, napi_value argValue, Drawing::Point& point)
337 {
338     GetPointXFromJsNumber(env, argValue, point);
339     GetPointYFromJsNumber(env, argValue, point);
340     return;
341 }
342 
343 void BindNativeFunction(napi_env env, napi_value object, const char* name, const char* moduleName, napi_callback func);
344 napi_value CreateJsError(napi_env env, int32_t errCode, const std::string& message);
345 
346 napi_value NapiThrowError(napi_env env, TextErrorCode err, const std::string& message);
347 
Str8ToStr16(const std::string & str)348 inline std::u16string Str8ToStr16(const std::string &str)
349 {
350     return std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> {}.from_bytes(str);
351 }
352 
SetDoubleValueFromJS(napi_env env,napi_value argValue,const std::string str,double & cValue)353 inline void SetDoubleValueFromJS(napi_env env, napi_value argValue, const std::string str, double& cValue)
354 {
355     napi_value tempValue = nullptr;
356     napi_get_named_property(env, argValue, str.c_str(), &tempValue);
357     if (tempValue == nullptr) {
358         return;
359     }
360     ConvertFromJsValue(env, tempValue, cValue);
361 }
362 
SetBoolValueFromJS(napi_env env,napi_value argValue,const std::string str,bool & cValue)363 inline void SetBoolValueFromJS(napi_env env, napi_value argValue, const std::string str, bool& cValue)
364 {
365     napi_value tempValue = nullptr;
366     napi_get_named_property(env, argValue, str.c_str(), &tempValue);
367     if (tempValue == nullptr) {
368         return;
369     }
370     ConvertFromJsValue(env, tempValue, cValue);
371 }
372 
GetPositionWithAffinityAndConvertToJsValue(napi_env env,IndexAndAffinity * positionWithAffinity)373 inline napi_value GetPositionWithAffinityAndConvertToJsValue(napi_env env,
374     IndexAndAffinity* positionWithAffinity)
375 {
376     napi_value objValue = nullptr;
377     napi_create_object(env, &objValue);
378     if (positionWithAffinity != nullptr && objValue != nullptr) {
379         napi_set_named_property(env, objValue, "position", CreateJsNumber(env, positionWithAffinity->index));
380         napi_set_named_property(env, objValue, "affinity", CreateJsNumber(env, (int)positionWithAffinity->affinity));
381     }
382     return objValue;
383 }
384 
GetRangeAndConvertToJsValue(napi_env env,Boundary * range)385 inline napi_value GetRangeAndConvertToJsValue(napi_env env, Boundary* range)
386 {
387     napi_value objValue = nullptr;
388     napi_create_object(env, &objValue);
389     if (range != nullptr && objValue != nullptr) {
390         napi_set_named_property(env, objValue, "start", CreateJsNumber(env, range->leftIndex));
391         napi_set_named_property(env, objValue, "end", CreateJsNumber(env, range->rightIndex));
392     }
393     return objValue;
394 }
395 
GetRectAndConvertToJsValue(napi_env env,Drawing::Rect rect)396 inline napi_value GetRectAndConvertToJsValue(napi_env env, Drawing::Rect rect)
397 {
398     napi_value objValue = nullptr;
399     napi_create_object(env, &objValue);
400     if (objValue != nullptr) {
401         napi_set_named_property(env, objValue, "left", CreateJsNumber(env, rect.GetLeft()));
402         napi_set_named_property(env, objValue, "top", CreateJsNumber(env, rect.GetTop()));
403         napi_set_named_property(env, objValue, "right", CreateJsNumber(env, rect.GetRight()));
404         napi_set_named_property(env, objValue, "bottom", CreateJsNumber(env, rect.GetBottom()));
405     }
406     return objValue;
407 }
408 
CreateTextRectJsValue(napi_env env,TextRect textrect)409 inline napi_value CreateTextRectJsValue(napi_env env, TextRect textrect)
410 {
411     napi_value objValue = nullptr;
412     napi_create_object(env, &objValue);
413     if (objValue != nullptr) {
414         napi_set_named_property(env, objValue, "rect", GetRectAndConvertToJsValue(env, textrect.rect));
415         napi_set_named_property(env, objValue, "direction", CreateJsNumber(env, (int)textrect.direction));
416     }
417     return objValue;
418 }
419 
CreateArrayStringJsValue(napi_env env,const std::vector<std::string> & vectorString)420 inline napi_value CreateArrayStringJsValue(napi_env env, const std::vector<std::string>& vectorString)
421 {
422     napi_value jsArray = nullptr;
423     if (napi_create_array_with_length(env, vectorString.size(), &jsArray) == napi_ok) {
424         size_t index = 0;
425         for (const auto& family : vectorString) {
426             napi_value jsString;
427             napi_create_string_utf8(env, family.c_str(), family.length(), &jsString);
428             napi_set_element(env, jsArray, index++, jsString);
429         }
430     }
431     return jsArray;
432 }
433 
CreateStringJsValue(napi_env env,std::u16string & u16String)434 inline napi_value CreateStringJsValue(napi_env env, std::u16string& u16String)
435 {
436     napi_value jsStr = nullptr;
437     napi_create_string_utf16(env, reinterpret_cast<const char16_t*>(u16String.c_str()), u16String.length(), &jsStr);
438     return jsStr;
439 }
440 
441 napi_value CreateTextStyleJsValue(napi_env env, TextStyle textStyle);
442 
443 napi_value CreateFontMetricsJsValue(napi_env env, Drawing::FontMetrics& fontMetrics);
444 
CreateRunMetricsJsValue(napi_env env,RunMetrics runMetrics)445 inline napi_value CreateRunMetricsJsValue(napi_env env, RunMetrics runMetrics)
446 {
447     napi_value objValue = nullptr;
448     napi_create_object(env, &objValue);
449     if (objValue != nullptr) {
450         napi_set_named_property(env, objValue, "textStyle", CreateTextStyleJsValue(env, *(runMetrics.textStyle)));
451         napi_set_named_property(env, objValue, "fontMetrics", CreateFontMetricsJsValue(env, runMetrics.fontMetrics));
452     }
453     return objValue;
454 }
455 
456 napi_value ConvertMapToNapiMap(napi_env env, const std::map<size_t, RunMetrics>& map);
457 
458 napi_value CreateLineMetricsJsValue(napi_env env, OHOS::Rosen::LineMetrics& lineMetrics);
459 
SetFontMetricsFloatValueFromJS(napi_env env,napi_value argValue,const std::string & str,float & cValue)460 inline void SetFontMetricsFloatValueFromJS(napi_env env, napi_value argValue, const std::string& str, float& cValue)
461 {
462     napi_value tempValue = nullptr;
463     napi_get_named_property(env, argValue, str.c_str(), &tempValue);
464     if (tempValue == nullptr) {
465         return;
466     }
467     double tempValuechild = 0.0;
468 
469     ConvertFromJsValue(env, tempValue, tempValuechild);
470     cValue = static_cast<float>(tempValuechild);
471 }
472 
SetLineMetricsDoubleValueFromJS(napi_env env,napi_value argValue,const std::string str,double & cValue)473 inline void SetLineMetricsDoubleValueFromJS(napi_env env, napi_value argValue, const std::string str, double& cValue)
474 {
475     napi_value tempValue = nullptr;
476     napi_get_named_property(env, argValue, str.c_str(), &tempValue);
477     if (tempValue == nullptr) {
478         return;
479     }
480     ConvertFromJsValue(env, tempValue, cValue);
481 }
482 
SetLineMetricsSizeTValueFromJS(napi_env env,napi_value argValue,const std::string str,size_t & cValue)483 inline void SetLineMetricsSizeTValueFromJS(napi_env env, napi_value argValue, const std::string str, size_t& cValue)
484 {
485     napi_value tempValue = nullptr;
486     napi_get_named_property(env, argValue, str.c_str(), &tempValue);
487     if (tempValue == nullptr) {
488         return;
489     }
490 
491     uint32_t tempValuechild = 0;
492     ConvertFromJsValue(env, tempValue, tempValuechild);
493     cValue = static_cast<size_t>(tempValuechild);
494 }
495 
496 bool OnMakeFontFamilies(napi_env& env, napi_value jsValue, std::vector<std::string> &fontFamilies);
497 
498 bool SetColorFromJS(napi_env env, napi_value argValue, const std::string& str, Drawing::Color& colorSrc);
499 
500 bool GetDecorationFromJS(napi_env env, napi_value argValue, TextStyle& textStyle);
501 
502 bool GetTextStyleFromJS(napi_env env, napi_value argValue, TextStyle& textStyle);
503 
504 bool GetParagraphStyleFromJS(napi_env env, napi_value argValue, TypographyStyle& pographyStyle);
505 
506 bool GetPlaceholderSpanFromJS(napi_env env, napi_value argValue, PlaceholderSpan& placeholderSpan);
507 
508 void ParsePartTextStyle(napi_env env, napi_value argValue, TextStyle& textStyle);
509 
510 void SetTextStyleBaseType(napi_env env, napi_value argValue, TextStyle& textStyle);
511 
512 void ReceiveFontFeature(napi_env env, napi_value argValue, TextStyle& textStyle);
513 
514 void ReceiveFontVariation(napi_env env, napi_value argValue, TextStyle& textStyle);
515 
516 size_t GetParamLen(napi_env env, napi_value param);
517 
518 bool GetFontMetricsFromJS(napi_env env, napi_value argValue, Drawing::FontMetrics& fontMetrics);
519 
520 bool GetRunMetricsFromJS(napi_env env, napi_value argValue, RunMetrics& runMetrics);
521 
522 bool GetNamePropertyFromJS(napi_env env, napi_value argValue, const std::string& str, napi_value& propertyValue);
523 
524 template<class Type>
SetEnumValueFromJS(napi_env env,napi_value argValue,const std::string str,Type & typeValue)525 void SetEnumValueFromJS(napi_env env, napi_value argValue, const std::string str, Type& typeValue)
526 {
527     napi_value propertyValue = nullptr;
528     if (!GetNamePropertyFromJS(env, argValue, str, propertyValue)) {
529         return;
530     }
531 
532     ConvertFromJsValue(env, propertyValue, typeValue);
533 }
534 
535 void ScanShadowValue(napi_env env, napi_value allShadowValue, uint32_t arrayLength, TextStyle& textStyle);
536 
537 void SetTextShadowProperty(napi_env env, napi_value argValue, TextStyle& textStyle);
538 
539 bool SetStrutStyleFromJS(napi_env env, napi_value strutStyleValue, TypographyStyle& typographyStyle);
540 
541 void SetRectStyleFromJS(napi_env env, napi_value argValue, RectStyle& rectStyle);
542 } // namespace OHOS::Rosen
543 #endif // OHOS_JS_TEXT_UTILS_H