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