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 "js_paragraph_builder.h"
17
18 #include <string>
19
20 #include "fontcollection_napi/js_fontcollection.h"
21 #include "js_native_api.h"
22 #include "line_typeset_napi/js_line_typeset.h"
23 #include "napi_common.h"
24 #include "paragraph_napi/js_paragraph.h"
25 #include "txt/text_bundle_config_parser.h"
26 #include "utils/string_util.h"
27 #include "utils/text_log.h"
28
29 namespace OHOS::Rosen {
30 namespace {
31 const std::string CLASS_NAME = "ParagraphBuilder";
32 }
33
34 thread_local napi_ref JsParagraphBuilder::constructor_ = nullptr;
35
Constructor(napi_env env,napi_callback_info info)36 napi_value JsParagraphBuilder::Constructor(napi_env env, napi_callback_info info)
37 {
38 size_t argCount = ARGC_TWO;
39 napi_value jsThis = nullptr;
40 napi_value argv[ARGC_TWO] = {nullptr};
41 napi_status status = napi_get_cb_info(env, info, &argCount, argv, &jsThis, nullptr);
42 if (status != napi_ok || argCount != ARGC_TWO) {
43 TEXT_LOGE("Failed to get parameter, argc %{public}zu, ret %{public}d", argCount, status);
44 return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
45 }
46 TypographyStyle typographyStyle;
47 GetParagraphStyleFromJS(env, argv[0], typographyStyle);
48 JsFontCollection* jsFontCollection = nullptr;
49 status = napi_unwrap(env, argv[1], reinterpret_cast<void**>(&jsFontCollection));
50
51 if (jsFontCollection == nullptr) {
52 return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
53 }
54 if (status != napi_ok) {
55 return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Get jsFontCollection error.");
56 }
57
58 std::shared_ptr<FontCollection> fontCollection = jsFontCollection->GetFontCollection();
59 std::unique_ptr<TypographyCreate> typographyCreate = TypographyCreate::Create(typographyStyle, fontCollection);
60 if (!typographyCreate) {
61 TEXT_LOGE("Failed to create typography creator");
62 return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "TypographyCreate Create error.");
63 }
64
65 JsParagraphBuilder* jsParagraphBuilder = new(std::nothrow) JsParagraphBuilder();
66 if (!jsParagraphBuilder) {
67 TEXT_LOGE("Failed to create paragraph builder");
68 return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "JsParagraphBuilder Create error.");
69 }
70 jsParagraphBuilder->SetTypographyCreate(std::move(typographyCreate));
71
72 status = napi_wrap(env, jsThis, jsParagraphBuilder,
73 JsParagraphBuilder::Destructor, nullptr, nullptr);
74 if (status != napi_ok) {
75 TEXT_LOGE("Failed to wrap paragraphy builder, ret %{public}d", status);
76 delete jsParagraphBuilder;
77 return nullptr;
78 }
79
80 return jsThis;
81 }
82
SetTypographyCreate(std::unique_ptr<TypographyCreate> typographyCreate)83 void JsParagraphBuilder::SetTypographyCreate(std::unique_ptr<TypographyCreate> typographyCreate)
84 {
85 typographyCreate_ = std::move(typographyCreate);
86 }
87
Init(napi_env env,napi_value exportObj)88 napi_value JsParagraphBuilder::Init(napi_env env, napi_value exportObj)
89 {
90 napi_property_descriptor properties[] = {
91 DECLARE_NAPI_FUNCTION("pushStyle", JsParagraphBuilder::PushStyle),
92 DECLARE_NAPI_FUNCTION("addText", JsParagraphBuilder::AddText),
93 DECLARE_NAPI_FUNCTION("popStyle", JsParagraphBuilder::PopStyle),
94 DECLARE_NAPI_FUNCTION("addPlaceholder", JsParagraphBuilder::AddPlaceholder),
95 DECLARE_NAPI_FUNCTION("build", JsParagraphBuilder::Build),
96 DECLARE_NAPI_FUNCTION("buildLineTypeset", JsParagraphBuilder::BuildLineTypeset),
97 DECLARE_NAPI_FUNCTION("addSymbol", JsParagraphBuilder::AppendSymbol),
98 };
99
100 napi_value constructor = nullptr;
101 napi_status status = napi_define_class(env, CLASS_NAME.c_str(), NAPI_AUTO_LENGTH, Constructor, nullptr,
102 sizeof(properties) / sizeof(properties[0]), properties, &constructor);
103 if (status != napi_ok) {
104 TEXT_LOGE("Failed to define class, ret %{public}d", status);
105 return nullptr;
106 }
107
108 status = napi_create_reference(env, constructor, 1, &constructor_);
109 if (status != napi_ok) {
110 TEXT_LOGE("Failed to create reference");
111 return nullptr;
112 }
113
114 status = napi_set_named_property(env, exportObj, CLASS_NAME.c_str(), constructor);
115 if (status != napi_ok) {
116 TEXT_LOGE("Failed to set named property, ret %{public}d", status);
117 return nullptr;
118 }
119 return exportObj;
120 }
121
Destructor(napi_env env,void * nativeObject,void * finalize)122 void JsParagraphBuilder::Destructor(napi_env env, void* nativeObject, void* finalize)
123 {
124 (void)finalize;
125 if (nativeObject != nullptr) {
126 JsParagraphBuilder* napi = reinterpret_cast<JsParagraphBuilder *>(nativeObject);
127 delete napi;
128 }
129 }
130
PushStyle(napi_env env,napi_callback_info info)131 napi_value JsParagraphBuilder::PushStyle(napi_env env, napi_callback_info info)
132 {
133 JsParagraphBuilder* me = CheckParamsAndGetThis<JsParagraphBuilder>(env, info);
134 return (me != nullptr) ? me->OnPushStyle(env, info) : nullptr;
135 }
136
OnPushStyle(napi_env env,napi_callback_info info)137 napi_value JsParagraphBuilder::OnPushStyle(napi_env env, napi_callback_info info)
138 {
139 if (typographyCreate_ == nullptr) {
140 return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
141 }
142 size_t argc = ARGC_ONE;
143 napi_value argv[ARGC_ONE] = {nullptr};
144 napi_status status = napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
145 if (status != napi_ok || argc < ARGC_ONE) {
146 return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
147 }
148 napi_valuetype valueType = napi_undefined;
149 if (argv[0] == nullptr || napi_typeof(env, argv[0], &valueType) != napi_ok || valueType != napi_object) {
150 return NapiGetUndefined(env);
151 }
152 TextStyle textStyle;
153 if (GetTextStyleFromJS(env, argv[0], textStyle)) {
154 typographyCreate_->PushStyle(textStyle);
155 }
156 return NapiGetUndefined(env);
157 }
158
AddText(napi_env env,napi_callback_info info)159 napi_value JsParagraphBuilder::AddText(napi_env env, napi_callback_info info)
160 {
161 JsParagraphBuilder* me = CheckParamsAndGetThis<JsParagraphBuilder>(env, info);
162 return (me != nullptr) ? me->OnAddText(env, info) : nullptr;
163 }
OnAddText(napi_env env,napi_callback_info info)164 napi_value JsParagraphBuilder::OnAddText(napi_env env, napi_callback_info info)
165 {
166 if (typographyCreate_ == nullptr) {
167 return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid typographyCreate");
168 }
169 size_t argc = ARGC_ONE;
170 napi_value argv[ARGC_ONE] = {nullptr};
171 napi_status status = napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
172 if (status != napi_ok || argc < ARGC_ONE) {
173 return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid param 0");
174 }
175 napi_valuetype valueType = napi_undefined;
176 if (argv[0] == nullptr || napi_typeof(env, argv[0], &valueType) != napi_ok) {
177 return NapiGetUndefined(env);
178 }
179
180 if (SPText::TextBundleConfigParser::GetInstance().IsTargetApiVersion(SPText::SINCE_API18_VERSION)) {
181 size_t len = 0;
182 if (napi_get_value_string_utf16(env, argv[0], nullptr, 0, &len) != napi_ok) {
183 TEXT_LOGE("Failed to get utf16 length");
184 return NapiGetUndefined(env);
185 }
186
187 auto buffer = std::make_unique<char16_t[]>(len + 1);
188 if (napi_get_value_string_utf16(env, argv[0], buffer.get(), len + 1, &len) != napi_ok) {
189 TEXT_LOGE("Failed to get utf16");
190 return NapiGetUndefined(env);
191 }
192 typographyCreate_->AppendText(std::u16string {buffer.get()});
193 } else {
194 std::string text = "";
195 if (ConvertFromJsValue(env, argv[0], text)) {
196 if (!IsUtf8(text.c_str(), text.size())) {
197 TEXT_LOGE("Invalid utf-8 text");
198 return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params");
199 }
200 typographyCreate_->AppendText(Str8ToStr16(text));
201 }
202 }
203 return NapiGetUndefined(env);
204 }
205
PopStyle(napi_env env,napi_callback_info info)206 napi_value JsParagraphBuilder::PopStyle(napi_env env, napi_callback_info info)
207 {
208 JsParagraphBuilder* me = CheckParamsAndGetThis<JsParagraphBuilder>(env, info);
209 return (me != nullptr) ? me->OnPopStyle(env, info) : nullptr;
210 }
211
OnPopStyle(napi_env env,napi_callback_info info)212 napi_value JsParagraphBuilder::OnPopStyle(napi_env env, napi_callback_info info)
213 {
214 if (typographyCreate_ == nullptr) {
215 return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
216 }
217 typographyCreate_->PopStyle();
218 return NapiGetUndefined(env);
219 }
220
AddPlaceholder(napi_env env,napi_callback_info info)221 napi_value JsParagraphBuilder::AddPlaceholder(napi_env env, napi_callback_info info)
222 {
223 JsParagraphBuilder* me = CheckParamsAndGetThis<JsParagraphBuilder>(env, info);
224 return (me != nullptr) ? me->OnAddPlaceholder(env, info) : nullptr;
225 }
226
OnAddPlaceholder(napi_env env,napi_callback_info info)227 napi_value JsParagraphBuilder::OnAddPlaceholder(napi_env env, napi_callback_info info)
228 {
229 if (typographyCreate_ == nullptr) {
230 TEXT_LOGE("Null typography creator");
231 return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
232 }
233 size_t argc = ARGC_ONE;
234 napi_value argv[ARGC_ONE] = {nullptr};
235 napi_status status = napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
236 if (status != napi_ok || argc < ARGC_ONE) {
237 TEXT_LOGE("Failed to get parameter, argc %{public}zu, ret %{public}d", argc, status);
238 return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
239 }
240 napi_valuetype valueType = napi_undefined;
241 if (argv[0] == nullptr || napi_typeof(env, argv[0], &valueType) != napi_ok || valueType != napi_object) {
242 TEXT_LOGE("Invalid argv[0]");
243 return NapiGetUndefined(env);
244 }
245 PlaceholderSpan placeholderSpan;
246 bool res = GetPlaceholderSpanFromJS(env, argv[0], placeholderSpan);
247 if (!res) {
248 TEXT_LOGE("Failed to get placeholder");
249 return NapiGetUndefined(env);
250 }
251 typographyCreate_->AppendPlaceholder(placeholderSpan);
252 return NapiGetUndefined(env);
253 }
254
Build(napi_env env,napi_callback_info info)255 napi_value JsParagraphBuilder::Build(napi_env env, napi_callback_info info)
256 {
257 JsParagraphBuilder* me = CheckParamsAndGetThis<JsParagraphBuilder>(env, info);
258 return (me != nullptr) ? me->OnBuild(env, info) : nullptr;
259 }
260
OnBuild(napi_env env,napi_callback_info info)261 napi_value JsParagraphBuilder::OnBuild(napi_env env, napi_callback_info info)
262 {
263 if (typographyCreate_ == nullptr) {
264 TEXT_LOGE("Null typography builder");
265 return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
266 }
267
268 std::unique_ptr<OHOS::Rosen::Typography> typography = typographyCreate_->CreateTypography();
269 return JsParagraph::CreateJsTypography(env, std::move(typography));
270 }
271
BuildLineTypeset(napi_env env,napi_callback_info info)272 napi_value JsParagraphBuilder::BuildLineTypeset(napi_env env, napi_callback_info info)
273 {
274 JsParagraphBuilder* me = CheckParamsAndGetThis<JsParagraphBuilder>(env, info);
275 return (me != nullptr) ? me->OnBuildLineTypeset(env, info) : nullptr;
276 }
277
OnBuildLineTypeset(napi_env env,napi_callback_info info)278 napi_value JsParagraphBuilder::OnBuildLineTypeset(napi_env env, napi_callback_info info)
279 {
280 if (typographyCreate_ == nullptr) {
281 TEXT_LOGE("Null typography creator");
282 return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
283 }
284 std::unique_ptr<OHOS::Rosen::LineTypography> lineTypography = typographyCreate_->CreateLineTypography();
285 if (lineTypography == nullptr) {
286 return NapiThrowError(env, TextErrorCode::ERROR_INVALID_PARAM, "Failed to create line typography.");
287 }
288 return JsLineTypeset::CreateJsLineTypeset(env, std::move(lineTypography));
289 }
290
AppendSymbol(napi_env env,napi_callback_info info)291 napi_value JsParagraphBuilder::AppendSymbol(napi_env env, napi_callback_info info)
292 {
293 JsParagraphBuilder* me = CheckParamsAndGetThis<JsParagraphBuilder>(env, info);
294 return (me != nullptr) ? me->OnAppendSymbol(env, info) : nullptr;
295 }
296
OnAppendSymbol(napi_env env,napi_callback_info info)297 napi_value JsParagraphBuilder::OnAppendSymbol(napi_env env, napi_callback_info info)
298 {
299 if (typographyCreate_ == nullptr) {
300 TEXT_LOGE("Null typography creator");
301 return nullptr;
302 }
303 size_t argc = ARGC_ONE;
304 napi_value argv[ARGC_ONE] = {nullptr};
305 napi_status status = napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
306 if ((status != napi_ok) || (argc < ARGC_ONE)) {
307 TEXT_LOGE("Failed to get parameter, argc %{public}zu, ret %{public}d", argc, status);
308 return nullptr;
309 }
310
311 uint32_t symbolId = 0;
312 if (!ConvertFromJsNumber(env, argv[0], symbolId)) {
313 TEXT_LOGE("Failed to convert symbol id");
314 return nullptr;
315 }
316 typographyCreate_->AppendSymbol(symbolId);
317 return NapiGetUndefined(env);
318 }
319 } // namespace OHOS::Rosen
320