1 /*
2  * Copyright (c) 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 <map>
17 #include <optional>
18 #include <string>
19 
20 #include "interfaces/napi/kits/utils/napi_utils.h"
21 #include "js_native_api.h"
22 #include "js_native_api_types.h"
23 #include "napi/native_api.h"
24 #include "napi/native_engine/native_value.h"
25 #include "napi/native_node_api.h"
26 
27 #include "base/geometry/dimension.h"
28 #include "base/geometry/size.h"
29 #include "bridge/common/utils/engine_helper.h"
30 #include "bridge/js_frontend/engine/common/js_engine.h"
31 #include "core/components/common/layout/constants.h"
32 #include "core/components/common/properties/text_style.h"
33 #include "frameworks/base/utils/measure_util.h"
34 
35 extern const char _binary_measure_js_start[];
36 extern const char _binary_measure_abc_start[];
37 #if !defined(IOS_PLATFORM)
38 extern const char _binary_measure_js_end[];
39 extern const char _binary_measure_abc_end[];
40 #else
41 extern const char* _binary_measure_js_end;
42 extern const char* _binary_measure_abc_end;
43 #endif
44 
45 namespace OHOS::Ace::Napi {
46 namespace {
MeasureStringToDimensionWithUnit(const std::string & value,bool & useDefaultUnit,DimensionUnit defaultUnit=DimensionUnit::PX,float defaultValue=0.0f,bool isCalc=false)47 Dimension MeasureStringToDimensionWithUnit(const std::string& value, bool& useDefaultUnit,
48     DimensionUnit defaultUnit = DimensionUnit::PX, float defaultValue = 0.0f, bool isCalc = false)
49 {
50     errno = 0;
51     if (std::strcmp(value.c_str(), "auto") == 0) {
52         return Dimension(defaultValue, DimensionUnit::AUTO);
53     }
54     char* pEnd = nullptr;
55     double result = std::strtod(value.c_str(), &pEnd);
56     if (pEnd == value.c_str() || errno == ERANGE) {
57         useDefaultUnit = true;
58         return Dimension(defaultValue, defaultUnit);
59     }
60     if (pEnd != nullptr) {
61         if (std::strcmp(pEnd, "%") == 0) {
62             // Parse percent, transfer from [0, 100] to [0, 1]
63             return Dimension(result / 100.0, DimensionUnit::PERCENT);
64         }
65         if (std::strcmp(pEnd, "px") == 0) {
66             return Dimension(result, DimensionUnit::PX);
67         }
68         if (std::strcmp(pEnd, "vp") == 0) {
69             return Dimension(result, DimensionUnit::VP);
70         }
71         if (std::strcmp(pEnd, "fp") == 0) {
72             return Dimension(result, DimensionUnit::FP);
73         }
74         if (std::strcmp(pEnd, "lpx") == 0) {
75             return Dimension(result, DimensionUnit::LPX);
76         }
77         if ((std::strcmp(pEnd, "\0") == 0) && isCalc) {
78             return Dimension(result, DimensionUnit::NONE);
79         }
80         if (isCalc) {
81             return Dimension(result, DimensionUnit::INVALID);
82         }
83     }
84     useDefaultUnit = true;
85     return Dimension(result, defaultUnit);
86 }
87 } // namespace
HandleIntStyle(napi_value fontStyleNApi,napi_env env)88 static int32_t HandleIntStyle(napi_value fontStyleNApi, napi_env env)
89 {
90     size_t ret = 0;
91     int32_t fontStyleInt = 0;
92     std::string fontStyleStr;
93     napi_valuetype valueType = napi_undefined;
94     napi_typeof(env, fontStyleNApi, &valueType);
95     if (valueType == napi_string) {
96         size_t fontStyleLen = GetParamLen(env, fontStyleNApi) + 1;
97         std::unique_ptr<char[]> fontStyleTemp = std::make_unique<char[]>(fontStyleLen);
98         napi_get_value_string_utf8(env, fontStyleNApi, fontStyleTemp.get(), fontStyleLen, &ret);
99         fontStyleStr = fontStyleTemp.get();
100         fontStyleInt = StringUtils::StringToInt(fontStyleStr);
101     } else if (valueType == napi_number) {
102         napi_get_value_int32(env, fontStyleNApi, &fontStyleInt);
103     } else if (valueType == napi_object) {
104         ResourceInfo recv;
105         if (!ParseResourceParam(env, fontStyleNApi, recv)) {
106             return fontStyleInt;
107         }
108         if (!ParseString(recv, fontStyleStr)) {
109             return fontStyleInt;
110         }
111         fontStyleInt = StringUtils::StringToInt(fontStyleStr);
112     } else {
113         return fontStyleInt;
114     }
115     return fontStyleInt;
116 }
117 
HandleStringType(napi_value ParameterNApi,napi_env env)118 static std::string HandleStringType(napi_value ParameterNApi, napi_env env)
119 {
120     size_t ret = 0;
121     std::string ParameterStr;
122     int32_t ParameterInt = 0;
123     napi_valuetype valueType = napi_undefined;
124     napi_typeof(env, ParameterNApi, &valueType);
125     if (valueType == napi_string) {
126         size_t ParameterLen = GetParamLen(env, ParameterNApi) + 1;
127         std::unique_ptr<char[]> Parameter = std::make_unique<char[]>(ParameterLen);
128         napi_get_value_string_utf8(env, ParameterNApi, Parameter.get(), ParameterLen, &ret);
129         ParameterStr = Parameter.get();
130     } else if (valueType == napi_number) {
131         napi_get_value_int32(env, ParameterNApi, &ParameterInt);
132         ParameterStr = std::to_string(ParameterInt);
133     } else if (valueType == napi_object) {
134         ResourceInfo recv;
135         if (!ParseResourceParam(env, ParameterNApi, recv)) {
136             return ParameterStr;
137         }
138         if (!ParseString(recv, ParameterStr)) {
139             return ParameterStr;
140         }
141     } else {
142         return ParameterStr;
143     }
144     return ParameterStr;
145 }
146 
HandleDimensionType(napi_value ParameterNApi,napi_env env,DimensionUnit defaultUnit,bool & useDefaultUnit)147 static std::optional<Dimension> HandleDimensionType(
148     napi_value ParameterNApi, napi_env env, DimensionUnit defaultUnit, bool& useDefaultUnit)
149 {
150     size_t ret = 0;
151     std::string ParameterStr;
152     napi_valuetype valueType = napi_undefined;
153     napi_typeof(env, ParameterNApi, &valueType);
154     Dimension Parameter;
155     if (valueType == napi_number) {
156         double ParameterValue;
157         napi_get_value_double(env, ParameterNApi, &ParameterValue);
158         Parameter.SetValue(ParameterValue);
159         Parameter.SetUnit(defaultUnit);
160         useDefaultUnit = true;
161     } else if (valueType == napi_string) {
162         size_t ParameterLen = GetParamLen(env, ParameterNApi) + 1;
163         std::unique_ptr<char[]> ParameterTemp = std::make_unique<char[]>(ParameterLen);
164         napi_get_value_string_utf8(env, ParameterNApi, ParameterTemp.get(), ParameterLen, &ret);
165         ParameterStr = ParameterTemp.get();
166         Parameter = MeasureStringToDimensionWithUnit(ParameterStr, useDefaultUnit, defaultUnit);
167     } else if (valueType == napi_object) {
168         ResourceInfo recv;
169         if (!ParseResourceParam(env, ParameterNApi, recv)) {
170             return std::nullopt;
171         }
172         if (!ParseString(recv, ParameterStr)) {
173             return std::nullopt;
174         }
175         if (!ParseIntegerToString(recv, ParameterStr)) {
176             return std::nullopt;
177         }
178         Parameter = MeasureStringToDimensionWithUnit(ParameterStr, useDefaultUnit, defaultUnit);
179     } else {
180         return std::nullopt;
181     }
182     return Parameter;
183 }
184 
JSMeasureText(napi_env env,napi_callback_info info)185 static napi_value JSMeasureText(napi_env env, napi_callback_info info)
186 {
187     size_t argc = 1;
188     napi_value result = nullptr;
189     napi_value argv = nullptr;
190     napi_value thisvar = nullptr;
191     void* data = nullptr;
192     napi_get_cb_info(env, info, &argc, &argv, &thisvar, &data);
193 
194     napi_value textContentNApi = nullptr;
195     napi_value fontSizeNApi = nullptr;
196     napi_value fontStyleNApi = nullptr;
197     napi_value fontWeightNApi = nullptr;
198     napi_value fontFamilyNApi = nullptr;
199     napi_value letterSpacingNApi = nullptr;
200 
201     napi_valuetype valueType = napi_undefined;
202     napi_typeof(env, argv, &valueType);
203     if (valueType == napi_object) {
204         napi_get_named_property(env, argv, "textContent", &textContentNApi);
205         napi_get_named_property(env, argv, "fontSize", &fontSizeNApi);
206         napi_get_named_property(env, argv, "fontStyle", &fontStyleNApi);
207         napi_get_named_property(env, argv, "fontWeight", &fontWeightNApi);
208         napi_get_named_property(env, argv, "fontFamily", &fontFamilyNApi);
209         napi_get_named_property(env, argv, "letterSpacing", &letterSpacingNApi);
210     } else {
211         return nullptr;
212     }
213     MeasureContext context;
214     auto isFontSizeUseDefaultUnit = false;
215     std::optional<Dimension> fontSizeNum =
216         HandleDimensionType(fontSizeNApi, env, DimensionUnit::FP, isFontSizeUseDefaultUnit);
217     context.isFontSizeUseDefaultUnit = isFontSizeUseDefaultUnit;
218     std::optional<Dimension> letterSpace =
219         HandleDimensionType(letterSpacingNApi, env, DimensionUnit::VP, isFontSizeUseDefaultUnit);
220     int32_t fontStyle = HandleIntStyle(fontStyleNApi, env);
221     std::string textContent = HandleStringType(textContentNApi, env);
222     std::string fontWeight = HandleStringType(fontWeightNApi, env);
223     std::string fontFamily = HandleStringType(fontFamilyNApi, env);
224     context.textContent = textContent;
225     context.fontSize = fontSizeNum;
226     context.fontStyle = static_cast<FontStyle>(fontStyle);
227     context.fontWeight = fontWeight;
228     context.fontFamily = fontFamily;
229     context.letterSpacing = letterSpace;
230     auto delegate = EngineHelper::GetCurrentDelegateSafely();
231     if (!delegate) {
232         return nullptr;
233     }
234     double textWidth = delegate->MeasureText(context);
235     napi_create_double(env, textWidth, &result);
236     return result;
237 }
238 
CreateMeasureTextSizeParamMap(std::map<std::string,napi_value> & contextParamMap)239 static void CreateMeasureTextSizeParamMap(std::map<std::string, napi_value>& contextParamMap)
240 {
241     napi_value textContentNApi = nullptr;
242     napi_value constraintWidthNApi = nullptr;
243     napi_value fontSizeNApi = nullptr;
244     napi_value fontStyleNApi = nullptr;
245     napi_value fontWeightNApi = nullptr;
246     napi_value fontFamilyNApi = nullptr;
247     napi_value letterSpacingNApi = nullptr;
248     napi_value textAlignNApi = nullptr;
249     napi_value textOverFlowNApi = nullptr;
250     napi_value maxLinesNApi = nullptr;
251     napi_value lineHeightNApi = nullptr;
252     napi_value baselineOffsetNApi = nullptr;
253     napi_value textCaseNApi = nullptr;
254     napi_value textIndentNApi = nullptr;
255     napi_value wordBreakNApi = nullptr;
256     contextParamMap["textContentNApi"] = textContentNApi;
257     contextParamMap["constraintWidthNApi"] = constraintWidthNApi;
258     contextParamMap["fontSizeNApi"] = fontSizeNApi;
259     contextParamMap["fontStyleNApi"] = fontStyleNApi;
260     contextParamMap["fontWeightNApi"] = fontWeightNApi;
261     contextParamMap["fontFamilyNApi"] = fontFamilyNApi;
262     contextParamMap["letterSpacingNApi"] = letterSpacingNApi;
263     contextParamMap["textAlignNApi"] = textAlignNApi;
264     contextParamMap["textOverFlowNApi"] = textOverFlowNApi;
265     contextParamMap["maxLinesNApi"] = maxLinesNApi;
266     contextParamMap["lineHeightNApi"] = lineHeightNApi;
267     contextParamMap["baselineOffsetNApi"] = baselineOffsetNApi;
268     contextParamMap["textCaseNApi"] = textCaseNApi;
269     contextParamMap["textIndentNApi"] = textIndentNApi;
270     contextParamMap["wordBreakNApi"] = wordBreakNApi;
271 }
272 
SetMeasureTextNapiProperty(std::map<std::string,napi_value> & contextParamMap,napi_value & argv,napi_env & env)273 static void SetMeasureTextNapiProperty(
274     std::map<std::string, napi_value>& contextParamMap, napi_value& argv, napi_env& env)
275 {
276     napi_get_named_property(env, argv, "textContent", &contextParamMap["textContentNApi"]);
277     napi_get_named_property(env, argv, "constraintWidth", &contextParamMap["constraintWidthNApi"]);
278     napi_get_named_property(env, argv, "fontSize", &contextParamMap["fontSizeNApi"]);
279     napi_get_named_property(env, argv, "fontStyle", &contextParamMap["fontStyleNApi"]);
280     napi_get_named_property(env, argv, "fontWeight", &contextParamMap["fontWeightNApi"]);
281     napi_get_named_property(env, argv, "fontFamily", &contextParamMap["fontFamilyNApi"]);
282     napi_get_named_property(env, argv, "letterSpacing", &contextParamMap["letterSpacingNApi"]);
283     napi_get_named_property(env, argv, "textAlign", &contextParamMap["textAlignNApi"]);
284     napi_get_named_property(env, argv, "overflow", &contextParamMap["textOverFlowNApi"]);
285     napi_get_named_property(env, argv, "maxLines", &contextParamMap["maxLinesNApi"]);
286     napi_get_named_property(env, argv, "lineHeight", &contextParamMap["lineHeightNApi"]);
287     napi_get_named_property(env, argv, "baselineOffset", &contextParamMap["baselineOffsetNApi"]);
288     napi_get_named_property(env, argv, "textCase", &contextParamMap["textCaseNApi"]);
289     napi_get_named_property(env, argv, "textIndent", &contextParamMap["textIndentNApi"]);
290     bool hasElement = false;
291     napi_has_named_property(env, argv, "wordBreak", &hasElement);
292     if (hasElement) {
293         napi_get_named_property(env, argv, "wordBreak", &contextParamMap["wordBreakNApi"]);
294     }
295 }
296 
SetContextProperty(std::map<std::string,napi_value> & contextParamMap,MeasureContext & context,napi_env & env)297 static void SetContextProperty(
298     std::map<std::string, napi_value>& contextParamMap, MeasureContext& context, napi_env& env)
299 {
300     auto isFontSizeUseDefaultUnit = false;
301     std::optional<Dimension> fontSizeNum =
302         HandleDimensionType(contextParamMap["fontSizeNApi"], env, DimensionUnit::FP, isFontSizeUseDefaultUnit);
303     context.isFontSizeUseDefaultUnit = isFontSizeUseDefaultUnit;
304     std::optional<Dimension> letterSpace =
305         HandleDimensionType(contextParamMap["letterSpacingNApi"], env, DimensionUnit::VP, isFontSizeUseDefaultUnit);
306     std::optional<Dimension> constraintWidth =
307         HandleDimensionType(contextParamMap["constraintWidthNApi"], env, DimensionUnit::VP, isFontSizeUseDefaultUnit);
308     std::optional<Dimension> lineHeight =
309         HandleDimensionType(contextParamMap["lineHeightNApi"], env, DimensionUnit::VP, isFontSizeUseDefaultUnit);
310     std::optional<Dimension> baselineOffset =
311         HandleDimensionType(contextParamMap["baselineOffsetNApi"], env, DimensionUnit::VP, isFontSizeUseDefaultUnit);
312     std::optional<Dimension> textIndent =
313         HandleDimensionType(contextParamMap["textIndentNApi"], env, DimensionUnit::VP, isFontSizeUseDefaultUnit);
314 
315     int32_t fontStyle = HandleIntStyle(contextParamMap["fontStyleNApi"], env);
316     int32_t textAlign = HandleIntStyle(contextParamMap["textAlignNApi"], env);
317     int32_t textOverFlow = HandleIntStyle(contextParamMap["textOverFlowNApi"], env);
318     int32_t maxlines = HandleIntStyle(contextParamMap["maxLinesNApi"], env);
319     int32_t textCase = HandleIntStyle(contextParamMap["textCaseNApi"], env);
320 
321     if (contextParamMap["wordBreakNApi"] != nullptr) {
322         napi_valuetype jsValueType = napi_undefined;
323         napi_typeof(env, contextParamMap["wordBreakNApi"], &jsValueType);
324         if (jsValueType != napi_undefined) {
325             int32_t wordBreak = HandleIntStyle(contextParamMap["wordBreakNApi"], env);
326             context.wordBreak = static_cast<WordBreak>(wordBreak);
327         }
328     }
329 
330     std::string textContent = HandleStringType(contextParamMap["textContentNApi"], env);
331     std::string fontWeight = HandleStringType(contextParamMap["fontWeightNApi"], env);
332     std::string fontFamily = HandleStringType(contextParamMap["fontFamilyNApi"], env);
333 
334     context.textContent = textContent;
335     context.constraintWidth = constraintWidth;
336     context.fontSize = fontSizeNum;
337     context.fontStyle = static_cast<FontStyle>(fontStyle);
338     context.fontWeight = fontWeight;
339     context.fontFamily = fontFamily;
340     context.letterSpacing = letterSpace;
341     context.textAlign = static_cast<TextAlign>(textAlign);
342     context.textOverlayFlow = static_cast<TextOverflow>(textOverFlow);
343     context.maxlines = maxlines;
344     context.lineHeight = lineHeight;
345     context.baselineOffset = baselineOffset;
346     context.textCase = static_cast<TextCase>(textCase);
347     context.textIndent = textIndent;
348 }
349 
JSMeasureTextSize(napi_env env,napi_callback_info info)350 static napi_value JSMeasureTextSize(napi_env env, napi_callback_info info)
351 {
352     size_t argc = 1;
353     napi_value result = nullptr;
354     napi_value argv = nullptr;
355     napi_value thisvar = nullptr;
356     void* data = nullptr;
357     napi_get_cb_info(env, info, &argc, &argv, &thisvar, &data);
358 
359     std::map<std::string, napi_value> contextParamMap;
360     CreateMeasureTextSizeParamMap(contextParamMap);
361     napi_valuetype valueType = napi_undefined;
362     napi_typeof(env, argv, &valueType);
363     MeasureContext context;
364     if (valueType == napi_object) {
365         SetMeasureTextNapiProperty(contextParamMap, argv, env);
366     } else {
367         return nullptr;
368     }
369     SetContextProperty(contextParamMap, context, env);
370     auto delegate = EngineHelper::GetCurrentDelegateSafely();
371     if (!delegate) {
372         return nullptr;
373     }
374     Size textSize = delegate->MeasureTextSize(context);
375 
376     napi_escapable_handle_scope scope = nullptr;
377     napi_open_escapable_handle_scope(env, &scope);
378     if (scope == nullptr) {
379         return result;
380     }
381 
382     napi_value resultArray[2] = { 0 };
383     napi_create_double(env, textSize.Width(), &resultArray[0]);
384     napi_create_double(env, textSize.Height(), &resultArray[1]);
385 
386     napi_create_object(env, &result);
387     napi_set_named_property(env, result, "width", resultArray[0]);
388     napi_set_named_property(env, result, "height", resultArray[1]);
389 
390     napi_value newResult = nullptr;
391     napi_escape_handle(env, scope, result, &newResult);
392     napi_close_escapable_handle_scope(env, scope);
393     return result;
394 }
395 
MeasureExport(napi_env env,napi_value exports)396 static napi_value MeasureExport(napi_env env, napi_value exports)
397 {
398     napi_property_descriptor measureDesc[] = {
399         DECLARE_NAPI_FUNCTION("measureText", JSMeasureText),
400         DECLARE_NAPI_FUNCTION("measureTextSize", JSMeasureTextSize),
401     };
402     NAPI_CALL(env, napi_define_properties(env, exports, sizeof(measureDesc) / sizeof(measureDesc[0]), measureDesc));
403     return exports;
404 }
405 
NAPI_measure_GetJSCode(const char ** buf,int * bufLen)406 extern "C" __attribute__((visibility("default"))) void NAPI_measure_GetJSCode(const char** buf, int* bufLen)
407 {
408     if (buf != nullptr) {
409         *buf = _binary_measure_js_start;
410     }
411 
412     if (bufLen != nullptr) {
413         *bufLen = _binary_measure_js_end - _binary_measure_js_start;
414     }
415 }
416 
NAPI_measure_GetABCCode(const char ** buf,int * buflen)417 extern "C" __attribute__((visibility("default"))) void NAPI_measure_GetABCCode(const char** buf, int* buflen)
418 {
419     if (buf != nullptr) {
420         *buf = _binary_measure_abc_start;
421     }
422     if (buflen != nullptr) {
423         *buflen = _binary_measure_abc_end - _binary_measure_abc_start;
424     }
425 }
426 
427 static napi_module_with_js measureModule = {
428     .nm_version = 1,
429     .nm_flags = 0,
430     .nm_filename = "libmeasure.z.so/measure.js",
431     .nm_register_func = MeasureExport,
432     .nm_modname = "measure",
433     .nm_priv = ((void*)0),
434     .nm_get_abc_code = NAPI_measure_GetABCCode,
435     .nm_get_js_code = NAPI_measure_GetJSCode,
436 };
437 
MeasureRegister()438 extern "C" __attribute__((constructor)) void MeasureRegister()
439 {
440     napi_module_with_js_register(&measureModule);
441 }
442 } // namespace OHOS::Ace::Napi
443