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 <string>
17 
18 #include "frameworks/bridge/common/utils/engine_helper.h"
19 #include "frameworks/bridge/declarative_frontend/engine/js_converter.h"
20 #include "frameworks/bridge/declarative_frontend/jsview/js_layout_manager.h"
21 #include "frameworks/bridge/declarative_frontend/jsview/js_text.h"
22 #include "native_engine/impl/ark/ark_native_engine.h"
23 
24 namespace OHOS::Ace::Framework {
25 
26 constexpr int32_t DEFAULT_LENGTH = 3;
GetLineCount(const JSCallbackInfo & args)27 void JSLayoutManager::GetLineCount(const JSCallbackInfo& args)
28 {
29     auto layoutInfoInterface = layoutInfoInterface_.Upgrade();
30     CHECK_NULL_VOID(layoutInfoInterface);
31     auto lineCount = layoutInfoInterface->GetLineCount();
32     auto vm = args.GetVm();
33     CHECK_NULL_VOID(vm);
34     auto lineCountObj = panda::NumberRef::New(vm, static_cast<int32_t>(lineCount));
35     args.SetReturnValue(JsiRef<JsiObject>(JsiObject(lineCountObj)));
36 }
37 
GetLineMetrics(const JSCallbackInfo & args)38 void JSLayoutManager::GetLineMetrics(const JSCallbackInfo& args)
39 {
40     auto layoutInfoInterface = layoutInfoInterface_.Upgrade();
41     CHECK_NULL_VOID(layoutInfoInterface);
42     if (args.Length() < 1 || args[0]->IsUndefined() || args[0]->IsNull() || !args[0]->IsNumber()) {
43         return;
44     }
45     if (double lineIndex = 0.0; !JSContainerBase::ParseJsDouble(args[0], lineIndex)
46         || lineIndex != static_cast<double>(static_cast<int>(lineIndex))) {
47         return;
48     }
49     int32_t lineIndex = 0;
50     JSViewAbstract::ParseJsInteger<int32_t>(args[0], lineIndex);
51     if (lineIndex < 0 || lineIndex >= static_cast<int32_t>(layoutInfoInterface->GetLineCount())) {
52         return;
53     }
54     auto lineMetrics = layoutInfoInterface->GetLineMetrics(lineIndex);
55 
56     auto vm = args.GetVm();
57     CHECK_NULL_VOID(vm);
58     const char* keysMetrics[] = { "startIndex", "endIndex", "ascent", "descent",
59         "height", "width", "left", "baseline", "lineNumber", "topHeight"};
60 
61     Local<JSValueRef> valuesOfMetrics[] = { panda::NumberRef::New(vm, static_cast<uint32_t>(lineMetrics.startIndex)),
62         panda::NumberRef::New(vm, static_cast<uint32_t>(lineMetrics.endIndex)),
63         panda::NumberRef::New(vm, lineMetrics.ascender),
64         panda::NumberRef::New(vm, lineMetrics.descender),
65         panda::NumberRef::New(vm, lineMetrics.height),
66         panda::NumberRef::New(vm, lineMetrics.width),
67         panda::NumberRef::New(vm, lineMetrics.x),
68         panda::NumberRef::New(vm, lineMetrics.baseline),
69         panda::NumberRef::New(vm, static_cast<uint32_t>(lineMetrics.lineNumber)),
70         panda::NumberRef::New(vm, lineMetrics.y)};
71 
72     auto lineMetricsObj = panda::ObjectRef::NewWithNamedProperties(
73         vm, ArraySize(keysMetrics), keysMetrics, valuesOfMetrics);
74     lineMetricsObj->Set(vm, panda::StringRef::NewFromUtf8(vm, "runMetrics"),
75         CreateJSRunMetrics(lineMetrics.runMetrics, args));
76 
77     args.SetReturnValue(JsiRef<JsiObject>(JsiObject(lineMetricsObj)));
78 }
79 
DidExceedMaxLines(const JSCallbackInfo & args)80 void JSLayoutManager::DidExceedMaxLines(const JSCallbackInfo& args)
81 {
82     auto layoutInfoInterface = layoutInfoInterface_.Upgrade();
83     CHECK_NULL_VOID(layoutInfoInterface);
84     auto exceedMaxLines = layoutInfoInterface->DidExceedMaxLines();
85 
86     auto vm = args.GetVm();
87     CHECK_NULL_VOID(vm);
88     auto exceedMaxLineObj = panda::BooleanRef::New(vm, exceedMaxLines);
89     args.SetReturnValue(JsiRef<JsiObject>(JsiObject(exceedMaxLineObj)));
90 }
91 
GetRectsForRange(const JSCallbackInfo & args)92 void JSLayoutManager::GetRectsForRange(const JSCallbackInfo& args)
93 {
94     if (args.Length() < DEFAULT_LENGTH) {
95         return;
96     }
97     auto textRangeVal = args[0];
98     if (!textRangeVal->IsObject() || textRangeVal->IsNull() || textRangeVal->IsUndefined()) {
99         return;
100     }
101     auto layoutInfoInterface = layoutInfoInterface_.Upgrade();
102     CHECK_NULL_VOID(layoutInfoInterface);
103     int32_t start = -1;
104     int32_t end = -1;
105     JSRef<JSObject> rangeObj = JSRef<JSObject>::Cast(textRangeVal);
106     JSRef<JSVal> startVal = rangeObj->GetProperty("start");
107     JSRef<JSVal> endVal = rangeObj->GetProperty("end");
108     if (!startVal->IsNull() && startVal->IsNumber()) {
109         start = startVal->ToNumber<int32_t>();
110     }
111     if (!endVal->IsNull() && endVal->IsNumber()) {
112         end = endVal->ToNumber<int32_t>();
113     }
114 
115     auto widthStyleVal = args[1];
116     if (widthStyleVal->IsNull() || widthStyleVal->IsUndefined()) {
117         return;
118     }
119     RectWidthStyle widthStyle = ParseRectWidthStyle(widthStyleVal.Get());
120     auto heightStyleVal = args[2];
121     if (heightStyleVal->IsNull() || heightStyleVal->IsUndefined()) {
122         return;
123     }
124     RectHeightStyle heightStyle = ParseRectHeightStyle(heightStyleVal.Get());
125     std::vector<NG::ParagraphManager::TextBox> textBoxes =
126         layoutInfoInterface->GetRectsForRange(start, end, heightStyle, widthStyle);
127     CHECK_NULL_VOID(&textBoxes);
128     JSRef<JSArray> textBoxArray = JSRef<JSArray>::New();
129     for (const auto& textBox : textBoxes) {
130         JSRef<JSObject> textBoxObj = JSRef<JSObject>::New();
131         JSRef<JSObject> rectObj = JSRef<JSObject>::New();
132         rectObj->SetProperty<float>("left", textBox.rect_.Left());
133         rectObj->SetProperty<float>("top", textBox.rect_.Top());
134         rectObj->SetProperty<float>("right", textBox.rect_.Right());
135         rectObj->SetProperty<float>("bottom", textBox.rect_.Bottom());
136         textBoxObj->SetPropertyObject("rect", rectObj);
137         textBoxObj->SetProperty<int32_t>("direction", static_cast<int32_t>(textBox.direction_));
138         textBoxArray->SetValueAt(textBoxArray->Length(), textBoxObj);
139     }
140     args.SetReturnValue(JSRef<JSVal>::Cast(textBoxArray));
141 }
142 
CreateJSRunMetrics(const std::map<size_t,RunMetrics> & mapRunMetrics,const JSCallbackInfo & args)143 Local<panda::ObjectRef> JSLayoutManager::CreateJSRunMetrics(const std::map<size_t,
144     RunMetrics>& mapRunMetrics, const JSCallbackInfo& args)
145 {
146     Local<panda::ObjectRef> obj;
147     auto vm = args.GetVm();
148     CHECK_NULL_RETURN(vm, obj);
149     auto mapRunMetricsObj = panda::MapRef::New(vm);
150 
151     for (const auto& [key, val] : mapRunMetrics) {
152         auto runMetricsObj = panda::ObjectRef::New(vm);
153         runMetricsObj->Set(vm, panda::StringRef::NewFromUtf8(vm, "textStyle"),
154             CreateJSTextStyleResult(val.textStyle, args));
155         runMetricsObj->Set(vm, panda::StringRef::NewFromUtf8(vm, "fontMetrics"),
156             CreateJSFontMetrics(val.fontMetrics, args));
157         mapRunMetricsObj->Set(vm, panda::NumberRef::New(vm, static_cast<uint32_t>(key)), runMetricsObj);
158     }
159     return mapRunMetricsObj;
160 }
161 
CreateJSFontMetrics(const FontMetrics & fontMetrics,const JSCallbackInfo & args)162 Local<panda::ObjectRef> JSLayoutManager::CreateJSFontMetrics(const FontMetrics& fontMetrics,
163     const JSCallbackInfo& args)
164 {
165     Local<panda::ObjectRef> obj;
166     auto vm = args.GetVm();
167     CHECK_NULL_RETURN(vm, obj);
168 
169     const char* keysFontMetrics[] = { "flags", "top", "ascent", "descent",
170         "bottom", "leading", "avgCharWidth", "maxCharWidth", "xMin", "xMax", "xHeight", "capHeight",
171         "underlineThickness", "underlinePosition", "strikethroughThickness", "strikethroughPosition"};
172 
173     Local<JSValueRef> valuesOfFontMetrics[] = { panda::NumberRef::New(vm, fontMetrics.fFlags),
174         panda::NumberRef::New(vm, fontMetrics.fTop),
175         panda::NumberRef::New(vm, fontMetrics.fAscent),
176         panda::NumberRef::New(vm, fontMetrics.fDescent),
177         panda::NumberRef::New(vm, fontMetrics.fBottom),
178         panda::NumberRef::New(vm, fontMetrics.fLeading),
179         panda::NumberRef::New(vm, fontMetrics.fAvgCharWidth),
180         panda::NumberRef::New(vm, fontMetrics.fMaxCharWidth),
181         panda::NumberRef::New(vm, fontMetrics.fXMin),
182         panda::NumberRef::New(vm, fontMetrics.fXMax),
183         panda::NumberRef::New(vm, fontMetrics.fXHeight),
184         panda::NumberRef::New(vm, fontMetrics.fCapHeight),
185         panda::NumberRef::New(vm, fontMetrics.fUnderlineThickness),
186         panda::NumberRef::New(vm, fontMetrics.fUnderlinePosition),
187         panda::NumberRef::New(vm, fontMetrics.fStrikeoutThickness),
188         panda::NumberRef::New(vm, fontMetrics.fStrikeoutPosition) };
189 
190     auto fontMetricsObj = panda::ObjectRef::NewWithNamedProperties(
191         vm, ArraySize(keysFontMetrics), keysFontMetrics, valuesOfFontMetrics);
192 
193     return fontMetricsObj;
194 }
195 
CreateJSTextStyleResult(const TextStyle & textStyle,const JSCallbackInfo & args)196 Local<panda::ObjectRef> JSLayoutManager::CreateJSTextStyleResult(const TextStyle& textStyle,
197     const JSCallbackInfo& args)
198 {
199     Local<panda::ObjectRef> obj;
200     auto vm = args.GetVm();
201     CHECK_NULL_RETURN(vm, obj);
202 
203     auto fontFamiliesArray = panda::ArrayRef::New(vm, textStyle.GetFontFamilies().size());
204     for (uint32_t i = 0; i<textStyle.GetFontFamilies().size(); i++) {
205         panda::ArrayRef::SetValueAt(vm, fontFamiliesArray, i, panda::StringRef::NewFromUtf8(
206             vm, textStyle.GetFontFamilies().at(i).c_str()));
207     }
208 
209     const char* keysTextStyle[] = { "decoration", "color", "fontWeight", "fontStyle", "baseline", "fontSize",
210         "letterSpacing", "wordSpacing", "heightScale", "halfLeading", "heightOnly", "ellipsisMode", "locale"};
211 
212     Local<JSValueRef> valuesOfFontMetrics[] = { panda::NumberRef::New(
213         vm, static_cast<int32_t>(textStyle.GetTextDecoration())),
214         panda::NumberRef::New(vm, textStyle.GetTextColor().GetValue()),
215         panda::NumberRef::New(vm, static_cast<int32_t>(textStyle.GetFontWeight())),
216         panda::NumberRef::New(vm, static_cast<int32_t>(textStyle.GetFontStyle())),
217         panda::NumberRef::New(vm, static_cast<int32_t>(textStyle.GetTextBaseline())),
218         panda::NumberRef::New(vm, textStyle.GetFontSize().Value()),
219         panda::NumberRef::New(vm, textStyle.GetLetterSpacing().Value()),
220         panda::NumberRef::New(vm, textStyle.GetWordSpacing().Value()),
221         panda::NumberRef::New(vm, textStyle.GetHeightScale()),
222         panda::NumberRef::New(vm, textStyle.GetHalfLeading()),
223         panda::NumberRef::New(vm, textStyle.GetHeightOnly()),
224         panda::StringRef::NewFromUtf8(vm, StringUtils::Str16ToStr8(textStyle.GetEllipsis()).c_str()),
225         panda::NumberRef::New(vm, static_cast<int32_t>(textStyle.GetEllipsisMode())),
226         panda::StringRef::NewFromUtf8(vm, textStyle.GetLocale().c_str()) };
227 
228     auto textStyleObj = panda::ObjectRef::NewWithNamedProperties(
229         vm, ArraySize(keysTextStyle), keysTextStyle, valuesOfFontMetrics);
230     textStyleObj->Set(vm, panda::StringRef::NewFromUtf8(vm, "fontFamilies"), fontFamiliesArray);
231     return textStyleObj;
232 }
233 
GetGlyphPositionAtCoordinate(const JSCallbackInfo & args)234 void JSLayoutManager::GetGlyphPositionAtCoordinate(const JSCallbackInfo& args)
235 {
236     if (args.Length() < 2) { // 2:At least two parameters
237         TAG_LOGE(AceLogTag::ACE_RICH_TEXT, "Info length error.");
238         return;
239     }
240     auto layoutInfoInterface = layoutInfoInterface_.Upgrade();
241     CHECK_NULL_VOID(layoutInfoInterface);
242     int32_t coordinateX = 0;
243     int32_t coordinateY = 0;
244     JSContainerBase::ParseJsInt32(args[0], coordinateX);
245     JSContainerBase::ParseJsInt32(args[1], coordinateY);
246     auto value = layoutInfoInterface->GetGlyphPositionAtCoordinate(coordinateX, coordinateY);
247 
248     auto vm = args.GetVm();
249     CHECK_NULL_VOID(vm);
250     auto positionObj = panda::NumberRef::New(vm, static_cast<int32_t>(value.position_));
251     auto affinityObj = panda::NumberRef::New(vm, static_cast<int32_t>(value.affinity_));
252 
253     auto positionWithAffinityObj = panda::ObjectRef::New(vm);
254     positionWithAffinityObj->Set(vm, panda::StringRef::NewFromUtf8(vm, "position"), positionObj);
255     positionWithAffinityObj->Set(vm, panda::StringRef::NewFromUtf8(vm, "affinity"), affinityObj);
256     args.SetReturnValue(JsiRef<JsiObject>(JsiObject(positionWithAffinityObj)));
257 }
258 
JSBind(BindingTarget globalObj)259 void JSLayoutManager::JSBind(BindingTarget globalObj)
260 {
261     JSClass<JSLayoutManager>::Declare("LayoutManager");
262     JSClass<JSLayoutManager>::CustomMethod("getLineCount", &JSLayoutManager::GetLineCount);
263     JSClass<JSLayoutManager>::CustomMethod(
264         "getGlyphPositionAtCoordinate", &JSLayoutManager::GetGlyphPositionAtCoordinate);
265     JSClass<JSLayoutManager>::CustomMethod("getLineMetrics", &JSLayoutManager::GetLineMetrics);
266     JSClass<JSLayoutManager>::CustomMethod("getRectsForRange", &JSLayoutManager::GetRectsForRange);
267     JSClass<JSLayoutManager>::CustomMethod("didExceedMaxLines", &JSLayoutManager::DidExceedMaxLines);
268     JSClass<JSLayoutManager>::Bind(globalObj, JSLayoutManager::Constructor, JSLayoutManager::Destructor);
269 }
270 } // namespace OHOS::Ace::Framework