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