• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 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 "canvas_napi/js_canvas.h"
17 #include "js_text_line.h"
18 #include "recording/recording_canvas.h"
19 #include "run_napi/js_run.h"
20 #include "utils/text_log.h"
21 
22 namespace OHOS::Rosen {
23 namespace {
24 const std::string CLASS_NAME = "TextLine";
25 }
26 
27 std::mutex JsTextLine::constructorMutex_;
28 thread_local napi_ref JsTextLine::constructor_ = nullptr;
29 
Constructor(napi_env env,napi_callback_info info)30 napi_value JsTextLine::Constructor(napi_env env, napi_callback_info info)
31 {
32     size_t argCount = 0;
33     napi_value jsThis = nullptr;
34     napi_status status = napi_get_cb_info(env, info, &argCount, nullptr, &jsThis, nullptr);
35     if (status != napi_ok) {
36         TEXT_LOGE("Failed to get parameter, ret %{public}d", status);
37         return nullptr;
38     }
39     JsTextLine *jsTextLineBase = new(std::nothrow) JsTextLine();
40     if (!jsTextLineBase) {
41         TEXT_LOGE("Failed to new text line");
42         return nullptr;
43     }
44     status = napi_wrap(env, jsThis, jsTextLineBase,
45                        JsTextLine::Destructor, nullptr, nullptr);
46     if (status != napi_ok) {
47         delete jsTextLineBase;
48         TEXT_LOGE("Failed to wrap text line, ret %{public}d", status);
49         return nullptr;
50     }
51     return jsThis;
52 }
53 
54 
Init(napi_env env,napi_value exportObj)55 napi_value JsTextLine::Init(napi_env env, napi_value exportObj)
56 {
57     if (!CreateConstructor(env)) {
58         TEXT_LOGE("Failed to create constructor");
59         return nullptr;
60     }
61     napi_value constructor = nullptr;
62     napi_status status = napi_get_reference_value(env, constructor_, &constructor);
63     if (status != napi_ok) {
64         TEXT_LOGE("Failed to get reference, ret %{public}d", status);
65         return nullptr;
66     }
67 
68     status = napi_set_named_property(env, exportObj, CLASS_NAME.c_str(), constructor);
69     if (status != napi_ok) {
70         TEXT_LOGE("Failed to set named property, ret %{public}d", status);
71         return nullptr;
72     }
73     return exportObj;
74 }
75 
CreateConstructor(napi_env env)76 bool JsTextLine::CreateConstructor(napi_env env)
77 {
78     std::lock_guard<std::mutex> lock(constructorMutex_);
79     if (constructor_) {
80         return true;
81     }
82     napi_property_descriptor properties[] = {
83         DECLARE_NAPI_FUNCTION("getGlyphCount", JsTextLine::GetGlyphCount),
84         DECLARE_NAPI_FUNCTION("getGlyphRuns", JsTextLine::GetGlyphRuns),
85         DECLARE_NAPI_FUNCTION("getTextRange", JsTextLine::GetTextRange),
86         DECLARE_NAPI_FUNCTION("paint", JsTextLine::Paint),
87         DECLARE_NAPI_FUNCTION("createTruncatedLine", JsTextLine::CreateTruncatedLine),
88         DECLARE_NAPI_FUNCTION("getTrailingSpaceWidth", JsTextLine::GetTrailingSpaceWidth),
89         DECLARE_NAPI_FUNCTION("getTypographicBounds", JsTextLine::GetTypographicBounds),
90         DECLARE_NAPI_FUNCTION("getImageBounds", JsTextLine::GetImageBounds),
91         DECLARE_NAPI_FUNCTION("getStringIndexForPosition", JsTextLine::GetStringIndexForPosition),
92         DECLARE_NAPI_FUNCTION("getOffsetForStringIndex", JsTextLine::GetOffsetForStringIndex),
93         DECLARE_NAPI_FUNCTION("enumerateCaretOffsets", JsTextLine::EnumerateCaretOffsets),
94         DECLARE_NAPI_FUNCTION("getAlignmentOffset", JsTextLine::GetAlignmentOffset),
95     };
96 
97     napi_value constructor = nullptr;
98     napi_status status = napi_define_class(env, CLASS_NAME.c_str(), NAPI_AUTO_LENGTH, Constructor, nullptr,
99         sizeof(properties) / sizeof(properties[0]), properties, &constructor);
100     if (status != napi_ok) {
101         TEXT_LOGE("Failed to define class, ret %{public}d", status);
102         return false;
103     }
104 
105     status = napi_create_reference(env, constructor, 1, &constructor_);
106     if (status != napi_ok) {
107         TEXT_LOGE("Failed to create reference, ret %{public}d", status);
108         return false;
109     }
110     return true;
111 }
112 
Destructor(napi_env env,void * nativeObject,void * finalize)113 void JsTextLine::Destructor(napi_env env, void *nativeObject, void *finalize)
114 {
115     (void)finalize;
116     if (nativeObject != nullptr) {
117         JsTextLine *napi = reinterpret_cast<JsTextLine *>(nativeObject);
118         delete napi;
119     }
120 }
121 
CreateTextLine(napi_env env,napi_callback_info info)122 napi_value JsTextLine::CreateTextLine(napi_env env, napi_callback_info info)
123 {
124     if (!CreateConstructor(env)) {
125         TEXT_LOGE("Failed to create constructor");
126         return nullptr;
127     }
128     napi_value result = nullptr;
129     napi_value constructor = nullptr;
130     napi_status status = napi_get_reference_value(env, constructor_, &constructor);
131     if (status != napi_ok) {
132         TEXT_LOGE("Failed to get reference, ret %{public}d", status);
133         return nullptr;
134     }
135 
136     status = napi_new_instance(env, constructor, 0, nullptr, &result);
137     if (status != napi_ok) {
138         TEXT_LOGE("Failed to new instance, ret %{public}d", status);
139         return nullptr;
140     }
141     return result;
142 }
143 
JsTextLine()144 JsTextLine::JsTextLine()
145 {
146 }
147 
SetTextLine(std::unique_ptr<TextLineBase> textLine)148 void JsTextLine::SetTextLine(std::unique_ptr<TextLineBase> textLine)
149 {
150     textLine_ = std::move(textLine);
151 }
152 
GetGlyphCount(napi_env env,napi_callback_info info)153 napi_value JsTextLine::GetGlyphCount(napi_env env, napi_callback_info info)
154 {
155     JsTextLine* me = CheckParamsAndGetThis<JsTextLine>(env, info);
156     return (me != nullptr) ? me->OnGetGlyphCount(env, info) : nullptr;
157 }
158 
GetGlyphRuns(napi_env env,napi_callback_info info)159 napi_value JsTextLine::GetGlyphRuns(napi_env env, napi_callback_info info)
160 {
161     JsTextLine* me = CheckParamsAndGetThis<JsTextLine>(env, info);
162     return (me != nullptr) ? me->OnGetGlyphRuns(env, info) : nullptr;
163 }
164 
GetTextRange(napi_env env,napi_callback_info info)165 napi_value JsTextLine::GetTextRange(napi_env env, napi_callback_info info)
166 {
167     JsTextLine* me = CheckParamsAndGetThis<JsTextLine>(env, info);
168     return (me != nullptr) ? me->OnGetTextRange(env, info) : nullptr;
169 }
170 
Paint(napi_env env,napi_callback_info info)171 napi_value JsTextLine::Paint(napi_env env, napi_callback_info info)
172 {
173     JsTextLine* me = CheckParamsAndGetThis<JsTextLine>(env, info);
174     return (me != nullptr) ? me->OnPaint(env, info) : nullptr;
175 }
176 
CreateTruncatedLine(napi_env env,napi_callback_info info)177 napi_value JsTextLine::CreateTruncatedLine(napi_env env, napi_callback_info info)
178 {
179     JsTextLine* me = CheckParamsAndGetThis<JsTextLine>(env, info);
180     return (me != nullptr) ? me->OnCreateTruncatedLine(env, info, constructor_) : nullptr;
181 }
182 
GetTypographicBounds(napi_env env,napi_callback_info info)183 napi_value JsTextLine::GetTypographicBounds(napi_env env, napi_callback_info info)
184 {
185     JsTextLine* me = CheckParamsAndGetThis<JsTextLine>(env, info);
186     return (me != nullptr) ? me->OnGetTypographicBounds(env, info) : nullptr;
187 }
188 
GetImageBounds(napi_env env,napi_callback_info info)189 napi_value JsTextLine::GetImageBounds(napi_env env, napi_callback_info info)
190 {
191     JsTextLine* me = CheckParamsAndGetThis<JsTextLine>(env, info);
192     return (me != nullptr) ? me->OnGetImageBounds(env, info) : nullptr;
193 }
194 
GetTrailingSpaceWidth(napi_env env,napi_callback_info info)195 napi_value JsTextLine::GetTrailingSpaceWidth(napi_env env, napi_callback_info info)
196 {
197     JsTextLine* me = CheckParamsAndGetThis<JsTextLine>(env, info);
198     return (me != nullptr) ? me->OnGetTrailingSpaceWidth(env, info) : nullptr;
199 }
200 
GetStringIndexForPosition(napi_env env,napi_callback_info info)201 napi_value JsTextLine::GetStringIndexForPosition(napi_env env, napi_callback_info info)
202 {
203     JsTextLine* me = CheckParamsAndGetThis<JsTextLine>(env, info);
204     return (me != nullptr) ? me->OnGetStringIndexForPosition(env, info) : nullptr;
205 }
206 
GetOffsetForStringIndex(napi_env env,napi_callback_info info)207 napi_value JsTextLine::GetOffsetForStringIndex(napi_env env, napi_callback_info info)
208 {
209     JsTextLine* me = CheckParamsAndGetThis<JsTextLine>(env, info);
210     return (me != nullptr) ? me->OnGetOffsetForStringIndex(env, info) : nullptr;
211 }
212 
EnumerateCaretOffsets(napi_env env,napi_callback_info info)213 napi_value JsTextLine::EnumerateCaretOffsets(napi_env env, napi_callback_info info)
214 {
215     JsTextLine* me = CheckParamsAndGetThis<JsTextLine>(env, info);
216     return (me != nullptr) ? me->OnEnumerateCaretOffsets(env, info) : nullptr;
217 }
218 
GetAlignmentOffset(napi_env env,napi_callback_info info)219 napi_value JsTextLine::GetAlignmentOffset(napi_env env, napi_callback_info info)
220 {
221     JsTextLine* me = CheckParamsAndGetThis<JsTextLine>(env, info);
222     return (me != nullptr) ? me->OnGetAlignmentOffset(env, info) : nullptr;
223 }
224 
OnGetGlyphCount(napi_env env,napi_callback_info info)225 napi_value JsTextLine::OnGetGlyphCount(napi_env env, napi_callback_info info)
226 {
227     if (textLine_ == nullptr) {
228         TEXT_LOGE("Null text line");
229         return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
230     }
231 
232     uint32_t textSize = textLine_->GetGlyphCount();
233     return CreateJsValue(env, textSize);
234 }
235 
OnGetGlyphRuns(napi_env env,napi_callback_info info)236 napi_value JsTextLine::OnGetGlyphRuns(napi_env env, napi_callback_info info)
237 {
238     if (textLine_ == nullptr) {
239         TEXT_LOGE("Null text line");
240         return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
241     }
242     std::vector<std::unique_ptr<Run>> runs = textLine_->GetGlyphRuns();
243     if (runs.empty()) {
244         TEXT_LOGE("Run is empty");
245         return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
246     }
247     napi_value array = nullptr;
248     NAPI_CALL(env, napi_create_array(env, &array));
249     uint32_t index = 0;
250     for (std::unique_ptr<Run>& item : runs) {
251         napi_value itemObject = JsRun::CreateRun(env, info);
252         if (!itemObject) {
253             TEXT_LOGE("Failed to create run");
254             continue;
255         }
256         JsRun* jsRun = nullptr;
257         napi_unwrap(env, itemObject, reinterpret_cast<void**>(&jsRun));
258         if (!jsRun) {
259             TEXT_LOGE("Failed to unwrap run");
260             continue;
261         }
262         jsRun->SetRun(std::move(item));
263         jsRun->SetParagraph(paragraph_);
264         napi_set_element(env, array, index++, itemObject);
265     }
266     return array;
267 }
268 
OnGetTextRange(napi_env env,napi_callback_info info)269 napi_value JsTextLine::OnGetTextRange(napi_env env, napi_callback_info info)
270 {
271     if (textLine_ == nullptr) {
272         TEXT_LOGE("Null text line");
273         return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
274     }
275 
276     Boundary boundary = textLine_->GetTextRange();
277     napi_value objValue = nullptr;
278     napi_create_object(env, &objValue);
279     if (objValue != nullptr) {
280         napi_set_named_property(env, objValue, "start", CreateJsNumber(env, boundary.leftIndex));
281         napi_set_named_property(env, objValue, "end", CreateJsNumber(env, boundary.rightIndex));
282     }
283     return objValue;
284 }
285 
OnPaint(napi_env env,napi_callback_info info)286 napi_value JsTextLine::OnPaint(napi_env env, napi_callback_info info)
287 {
288     if (textLine_ == nullptr) {
289         TEXT_LOGE("Null text line");
290         return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
291     }
292     size_t argc = ARGC_THREE;
293     napi_value argv[ARGC_THREE] = {nullptr};
294     napi_status status = napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
295     if (status != napi_ok || argc < ARGC_THREE) {
296         TEXT_LOGE("Failed to get paramter, argc %{public}zu, ret %{public}d", argc, status);
297         return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
298     }
299     if (argv[0] == nullptr) {
300         TEXT_LOGE("Null argv[0]");
301         return NapiGetUndefined(env);
302     }
303     Drawing::JsCanvas* jsCanvas = nullptr;
304     napi_unwrap(env, argv[0], reinterpret_cast<void**>(&jsCanvas));
305     if (!jsCanvas || !jsCanvas->GetCanvas()) {
306         TEXT_LOGE("Failed to get canvas");
307         return NapiGetUndefined(env);
308     }
309     double x = 0.0;
310     double y = 0.0;
311     if (!(argv[ARGC_ONE] != nullptr && ConvertFromJsValue(env, argv[ARGC_ONE], x) &&
312         argv[ARGC_TWO] != nullptr && ConvertFromJsValue(env, argv[ARGC_TWO], y))) {
313         return NapiGetUndefined(env);
314     }
315     textLine_->Paint(jsCanvas->GetCanvas(), x, y);
316 
317     return NapiGetUndefined(env);
318 }
319 
OnCreateTruncatedLine(napi_env env,napi_callback_info info,napi_ref constructor)320 napi_value JsTextLine::OnCreateTruncatedLine(napi_env env, napi_callback_info info, napi_ref constructor)
321 {
322     if (textLine_ == nullptr) {
323         TEXT_LOGE("Null text line");
324         return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
325     }
326 
327     size_t argc = ARGC_THREE;
328     napi_value argv[ARGC_THREE] = {nullptr};
329     napi_status status = napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
330     if (status != napi_ok || argc < ARGC_THREE) {
331         TEXT_LOGE("Failed to get argc(%{public}zu)", argc);
332         return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
333     }
334 
335     double width = 0.0;
336     if (!ConvertFromJsValue(env, argv[0], width)) {
337         TEXT_LOGE("Failed to convert width");
338         return NapiGetUndefined(env);
339     }
340     uint32_t ellipsisMode = 0;
341     if (!ConvertFromJsValue(env, argv[ARGC_ONE], ellipsisMode)) {
342         TEXT_LOGE("Failed to convert ellipsisMode");
343         return NapiGetUndefined(env);
344     }
345     std::string ellipsisStr = "";
346     if (!ConvertFromJsValue(env, argv[ARGC_TWO], ellipsisStr)) {
347         TEXT_LOGE("Failed to convert ellipsisStr");
348         return NapiGetUndefined(env);
349     }
350 
351     std::unique_ptr<TextLineBase> textLine = textLine_->CreateTruncatedLine(width, EllipsisModal(ellipsisMode),
352         ellipsisStr);
353     if (textLine == nullptr) {
354         TEXT_LOGE("Failed to create truncated textLine");
355         return NapiGetUndefined(env);
356     }
357 
358     napi_value itemObject = JsTextLine::CreateTextLine(env, info);
359     if (itemObject == nullptr) {
360         TEXT_LOGE("Failed to create js textLine");
361         return NapiGetUndefined(env);
362     }
363 
364     JsTextLine* jsTextLine = nullptr;
365     status = napi_unwrap(env, itemObject, reinterpret_cast<void**>(&jsTextLine));
366     if (status != napi_ok || jsTextLine == nullptr) {
367         TEXT_LOGE("Failed to napi_unwrap jsTextLine");
368         return NapiGetUndefined(env);
369     }
370     jsTextLine->SetTextLine(std::move(textLine));
371     jsTextLine->SetParagraph(paragraph_);
372 
373     return itemObject;
374 }
375 
OnGetTypographicBounds(napi_env env,napi_callback_info info)376 napi_value JsTextLine::OnGetTypographicBounds(napi_env env, napi_callback_info info)
377 {
378     if (textLine_ == nullptr) {
379         TEXT_LOGE("Null text line");
380         return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
381     }
382 
383     double ascent = 0.0;
384     double descent = 0.0;
385     double leading = 0.0;
386     double width = textLine_->GetTypographicBounds(&ascent, &descent, &leading);
387 
388     napi_value objValue = nullptr;
389     napi_status status = napi_create_object(env, &objValue);
390     if (status != napi_ok || objValue == nullptr) {
391         TEXT_LOGE("Failed to create object, ret %{public}d", static_cast<int>(status));
392         return NapiGetUndefined(env);
393     }
394 
395     status = napi_set_named_property(env, objValue, "ascent", CreateJsNumber(env, ascent));
396     if (status != napi_ok) {
397         TEXT_LOGE("Failed to set ascent, ret %{public}d", static_cast<int>(status));
398         return NapiGetUndefined(env);
399     }
400     status = napi_set_named_property(env, objValue, "descent", CreateJsNumber(env, descent));
401     if (status != napi_ok) {
402         TEXT_LOGE("Failed to set descent, ret %{public}d", static_cast<int>(status));
403         return NapiGetUndefined(env);
404     }
405     status = napi_set_named_property(env, objValue, "leading", CreateJsNumber(env, leading));
406     if (status != napi_ok) {
407         TEXT_LOGE("Failed to set leading, ret %{public}d", static_cast<int>(status));
408         return NapiGetUndefined(env);
409     }
410     status = napi_set_named_property(env, objValue, "width", CreateJsNumber(env, width));
411     if (status != napi_ok) {
412         TEXT_LOGE("Failed to set width, ret %{public}d", static_cast<int>(status));
413         return NapiGetUndefined(env);
414     }
415 
416     return objValue;
417 }
418 
OnGetImageBounds(napi_env env,napi_callback_info info)419 napi_value JsTextLine::OnGetImageBounds(napi_env env, napi_callback_info info)
420 {
421     if (textLine_ == nullptr) {
422         TEXT_LOGE("Null text line");
423         return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
424     }
425 
426     Drawing::Rect rect = textLine_->GetImageBounds();
427     return GetRectAndConvertToJsValue(env, rect);
428 }
429 
OnGetTrailingSpaceWidth(napi_env env,napi_callback_info info)430 napi_value JsTextLine::OnGetTrailingSpaceWidth(napi_env env, napi_callback_info info)
431 {
432     if (textLine_ == nullptr) {
433         TEXT_LOGE("Null text line");
434         return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
435     }
436 
437     double width = textLine_->GetTrailingSpaceWidth();
438     return CreateJsValue(env, width);
439 }
440 
OnGetStringIndexForPosition(napi_env env,napi_callback_info info)441 napi_value JsTextLine::OnGetStringIndexForPosition(napi_env env, napi_callback_info info)
442 {
443     if (textLine_ == nullptr) {
444         TEXT_LOGE("Null text line");
445         return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
446     }
447     size_t argc = ARGC_ONE;
448     napi_value argv[ARGC_ONE] = {nullptr};
449     napi_status status = napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
450     if (status != napi_ok || argc < ARGC_ONE) {
451         TEXT_LOGE("Failed to get argc(%{public}zu)", argc);
452         return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
453     }
454 
455     double x = 0.0;
456     double y = 0.0;
457     napi_value tempValue = nullptr;
458     status = napi_get_named_property(env, argv[0], "x", &tempValue);
459     if (status != napi_ok || tempValue == nullptr) {
460         TEXT_LOGE("Failed to get x, ret %{public}d", static_cast<int>(status));
461         return NapiGetUndefined(env);
462     }
463     if (!ConvertFromJsValue(env, tempValue, x)) {
464         TEXT_LOGE("Failed to convert x");
465         return NapiGetUndefined(env);
466     }
467     status = napi_get_named_property(env, argv[0], "y", &tempValue);
468     if (status != napi_ok || tempValue == nullptr) {
469         TEXT_LOGE("Failed to get y, ret %{public}d", static_cast<int>(status));
470         return NapiGetUndefined(env);
471     }
472     if (!ConvertFromJsValue(env, tempValue, y)) {
473         TEXT_LOGE("Failed to convert y");
474         return NapiGetUndefined(env);
475     }
476 
477     SkPoint point = {x, y};
478     int32_t index = textLine_->GetStringIndexForPosition(point);
479     return CreateJsValue(env, index);
480 }
481 
OnGetOffsetForStringIndex(napi_env env,napi_callback_info info)482 napi_value JsTextLine::OnGetOffsetForStringIndex(napi_env env, napi_callback_info info)
483 {
484     if (textLine_ == nullptr) {
485         TEXT_LOGE("Null text line");
486         return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
487     }
488     size_t argc = ARGC_ONE;
489     napi_value argv[ARGC_ONE] = {nullptr};
490     napi_status status = napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
491     if (status != napi_ok || argc < ARGC_ONE) {
492         TEXT_LOGE("Failed to get argc(%{public}zu)", argc);
493         return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
494     }
495 
496     int32_t index = 0;
497     if (!ConvertFromJsValue(env, argv[0], index)) {
498         TEXT_LOGE("Failed to convert index");
499         return NapiGetUndefined(env);
500     }
501 
502     double offset = textLine_->GetOffsetForStringIndex(index);
503     return CreateJsValue(env, offset);
504 }
505 
CallJsFunc(napi_env env,napi_value callback,int32_t index,double leftOffset,double rightOffset)506 bool CallJsFunc(napi_env env, napi_value callback, int32_t index, double leftOffset, double rightOffset)
507 {
508     napi_value jsLeadingEdgeTrue = CreateJsValue(env, true);
509     napi_value jsLeadingEdgeFalse = CreateJsValue(env, false);
510     napi_value jsIndex = CreateJsValue(env, index);
511     for (size_t i = 0; i < ARGC_TWO; i++) {
512         napi_value jsOffset = (i == 0) ? CreateJsValue(env, leftOffset) : CreateJsValue(env, rightOffset);
513         napi_value jsLeadingEdge = (i == 0) ? jsLeadingEdgeTrue : jsLeadingEdgeFalse;
514         napi_value retVal = nullptr;
515         napi_value params[ARGC_THREE] = {jsOffset, jsIndex, jsLeadingEdge};
516         napi_status status = napi_call_function(env, nullptr, callback, ARGC_THREE, params, &retVal);
517         if (status != napi_ok) {
518             TEXT_LOGE("Failed to napi_call_function");
519             return false;
520         }
521 
522         bool stop = false;
523         if (!ConvertFromJsValue(env, retVal, stop)) {
524             TEXT_LOGE("Failed to convert stop");
525             return false;
526         }
527         if (stop) {
528             TEXT_LOGI("Js call stoped");
529             return false;
530         }
531     }
532 
533     return true;
534 }
535 
OnEnumerateCaretOffsets(napi_env env,napi_callback_info info)536 napi_value JsTextLine::OnEnumerateCaretOffsets(napi_env env, napi_callback_info info)
537 {
538     TEXT_ERROR_CHECK(textLine_ != nullptr,
539         return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params."), "TextLine is nullptr");
540 
541     size_t argc = ARGC_ONE;
542     napi_value argv[ARGC_ONE];
543     napi_value jsCallback = nullptr;
544     napi_status status = napi_get_cb_info(env, info, &argc, argv, &jsCallback, nullptr);
545     if (status != napi_ok || argc < ARGC_ONE) {
546         TEXT_LOGE("Failed to get argc(%{public}zu)", argc);
547         return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
548     }
549     napi_valuetype valueType = napi_undefined;
550     status = napi_typeof(env, argv[0], &valueType);
551     if (status != napi_ok || valueType != napi_function) {
552         TEXT_LOGE("Failed to get napi type or argc is not function");
553         return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
554     }
555     napi_ref refCallback = nullptr;
556     std::unique_ptr<napi_ref, std::function<void(napi_ref*)>> refCallbackGuard(&refCallback, [env](napi_ref* ref) {
557         if (ref != nullptr && *ref != nullptr) {
558             TEXT_CHECK(napi_delete_reference(env, *ref) == napi_ok, TEXT_LOGE("Failed to release ref callback"));
559         }
560     });
561     status = napi_create_reference(env, argv[0], 1, &refCallback);
562     if (status != napi_ok) {
563         TEXT_LOGE("Failed to create reference");
564         return NapiGetUndefined(env);
565     }
566 
567     napi_value callback = nullptr;
568     if ((napi_get_reference_value(env, refCallback, &callback)) != napi_ok) {
569         TEXT_LOGE("Failed to get reference");
570         return NapiGetUndefined(env);
571     }
572 
573     bool isHardBreak = false;
574     std::map<int32_t, double> offsetMap = textLine_->GetIndexAndOffsets(isHardBreak);
575     double leftOffset = 0.0;
576     for (auto it = offsetMap.begin(); it != offsetMap.end(); ++it) {
577         if (!CallJsFunc(env, callback, it->first, leftOffset, it->second)) {
578             return NapiGetUndefined(env);
579         }
580         leftOffset = it->second;
581     }
582     if (isHardBreak && offsetMap.size() > 0) {
583         if (!CallJsFunc(env, callback, offsetMap.rbegin()->first + 1, leftOffset, leftOffset)) {
584             return NapiGetUndefined(env);
585         }
586     }
587 
588     return NapiGetUndefined(env);
589 }
590 
OnGetAlignmentOffset(napi_env env,napi_callback_info info)591 napi_value JsTextLine::OnGetAlignmentOffset(napi_env env, napi_callback_info info)
592 {
593     if (textLine_ == nullptr) {
594         TEXT_LOGE("Null text line");
595         return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
596     }
597 
598     size_t argc = ARGC_TWO;
599     napi_value argv[ARGC_TWO] = {nullptr};
600     napi_status status = napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
601     if (status != napi_ok || argc < ARGC_TWO) {
602         TEXT_LOGE("Failed to get argc(%{public}zu)", argc);
603         return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
604     }
605 
606     double alignmentFactor = 0.0;
607     if (!ConvertFromJsValue(env, argv[0], alignmentFactor)) {
608         TEXT_LOGE("Failed to convert alignmentFactor");
609         return NapiGetUndefined(env);
610     }
611     double alignmentWidth = 0.0;
612     if (!ConvertFromJsValue(env, argv[ARGC_ONE], alignmentWidth)) {
613         TEXT_LOGE("Failed to convert alignmentWidth");
614         return NapiGetUndefined(env);
615     }
616 
617     double offset = textLine_->GetAlignmentOffset(alignmentFactor, alignmentWidth);
618     return CreateJsValue(env, offset);
619 }
620 
GetTextLineBase()621 std::unique_ptr<TextLineBase> JsTextLine::GetTextLineBase()
622 {
623     return std::move(textLine_);
624 }
625 
SetParagraph(std::shared_ptr<Typography> paragraph)626 void JsTextLine::SetParagraph(std::shared_ptr<Typography> paragraph)
627 {
628     paragraph_ = paragraph;
629 }
630 } // namespace OHOS::Rosen