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