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 21extern const char _binary_measure_js_start[]; 22extern const char _binary_measure_abc_start[]; 23#if !defined(IOS_PLATFORM) 24extern const char _binary_measure_js_end[]; 25extern const char _binary_measure_abc_end[]; 26#else 27extern const char* _binary_measure_js_end; 28extern const char* _binary_measure_abc_end; 29#endif 30 31namespace OHOS::Ace::Napi { 32namespace { 33Dimension 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 74static 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 104static 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 133static 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 171static 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 225static 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 259static 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 283static 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 336static 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 382static 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 392extern "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 403extern "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 413static 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 424extern "C" __attribute__((constructor)) void MeasureRegister() 425{ 426 napi_module_with_js_register(&measureModule); 427} 428} // namespace OHOS::Ace::Napi 429