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