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_text_blob.h"
17 #include "../js_drawing_utils.h"
18 #include "native_value.h"
19 #include "../font_napi/js_font.h"
20 #include "../js_common.h"
21
22 namespace OHOS::Rosen {
23 namespace Drawing {
24 const std::string CLASS_NAME = "TextBlob";
25 thread_local napi_ref JsTextBlob::constructor_ = nullptr;
26 std::shared_ptr<TextBlob> drawingTextBlob;
Init(napi_env env,napi_value exportObj)27 napi_value JsTextBlob::Init(napi_env env, napi_value exportObj)
28 {
29 napi_property_descriptor properties[] = {
30 DECLARE_NAPI_FUNCTION("bounds", JsTextBlob::Bounds),
31 DECLARE_NAPI_STATIC_FUNCTION("makeFromString", JsTextBlob::MakeFromString),
32 DECLARE_NAPI_STATIC_FUNCTION("makeFromRunBuffer", JsTextBlob::MakeFromRunBuffer),
33 };
34
35 napi_value constructor = nullptr;
36 napi_status status = napi_define_class(env, CLASS_NAME.c_str(), NAPI_AUTO_LENGTH, Constructor, nullptr,
37 sizeof(properties) / sizeof(properties[0]), properties, &constructor);
38 if (status != napi_ok) {
39 ROSEN_LOGE("Drawing_napi: Failed to define TextBlob class");
40 return nullptr;
41 }
42
43 status = napi_create_reference(env, constructor, 1, &constructor_);
44 if (status != napi_ok) {
45 ROSEN_LOGE("Drawing_napi: Failed to create reference of constructor");
46 return nullptr;
47 }
48
49 status = napi_set_named_property(env, exportObj, CLASS_NAME.c_str(), constructor);
50 if (status != napi_ok) {
51 ROSEN_LOGE("Drawing_napi: Failed to set constructor");
52 return nullptr;
53 }
54
55 status = napi_define_properties(env, exportObj, sizeof(properties) / sizeof(properties[0]), properties);
56 if (status != napi_ok) {
57 ROSEN_LOGE("Drawing_napi: Failed to define static function");
58 return nullptr;
59 }
60 return exportObj;
61 }
62
~JsTextBlob()63 JsTextBlob::~JsTextBlob()
64 {
65 m_textBlob = nullptr;
66 }
67
Constructor(napi_env env,napi_callback_info info)68 napi_value JsTextBlob::Constructor(napi_env env, napi_callback_info info)
69 {
70 size_t argCount = 0;
71 napi_value jsThis = nullptr;
72 napi_status status = napi_get_cb_info(env, info, &argCount, nullptr, &jsThis, nullptr);
73 if (status != napi_ok) {
74 ROSEN_LOGE("failed to napi_get_cb_info");
75 return nullptr;
76 }
77
78 JsTextBlob *jsTextBlob = new(std::nothrow) JsTextBlob(env, drawingTextBlob);
79
80 status = napi_wrap(env, jsThis, jsTextBlob,
81 JsTextBlob::Destructor, nullptr, nullptr);
82 if (status != napi_ok) {
83 delete jsTextBlob;
84 ROSEN_LOGE("Failed to wrap native instance");
85 return nullptr;
86 }
87 return jsThis;
88 }
89
Destructor(napi_env env,void * nativeObject,void * finalize)90 void JsTextBlob::Destructor(napi_env env, void *nativeObject, void *finalize)
91 {
92 (void)finalize;
93 if (nativeObject != nullptr) {
94 JsTextBlob *napi = reinterpret_cast<JsTextBlob *>(nativeObject);
95 delete napi;
96 }
97 }
98
MakeFromRunBuffer(napi_env env,napi_callback_info info)99 napi_value JsTextBlob::MakeFromRunBuffer(napi_env env, napi_callback_info info)
100 {
101 size_t argc = ARGC_THREE;
102 napi_value argv[ARGC_THREE] = {nullptr};
103 napi_status status = napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
104 if (status != napi_ok || argc < ARGC_TWO || argc > ARGC_THREE) {
105 ROSEN_LOGE("JsTextBlob::MakeFromRunBuffer Argc is invalid: %{public}zu", argc);
106 return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
107 }
108
109 napi_value array = argv[0];
110 uint32_t size = 0;
111 napi_get_array_length(env, array, &size);
112
113 void* pointerResult = nullptr;
114 napi_unwrap(env, argv[1], &pointerResult);
115 auto jsFont = static_cast<JsFont*>(pointerResult);
116 if (jsFont == nullptr) {
117 ROSEN_LOGE("JsTextBlob::MakeFromRunBuffer jsFont is nullptr");
118 return nullptr;
119 }
120 std::shared_ptr<Font> font = jsFont->GetFont();
121 if (font == nullptr) {
122 ROSEN_LOGE("JsTextBlob::MakeFromRunBuffer font is nullptr");
123 return nullptr;
124 }
125
126 TextBlobBuilder::RunBuffer runBuffer;
127 std::shared_ptr<TextBlobBuilder> textBlobBuilder = std::make_shared<TextBlobBuilder>();
128 if (argc == ARGC_TWO) {
129 runBuffer = textBlobBuilder->AllocRunPos(*font, size);
130 } else {
131 Rect drawingRect;
132 napi_valuetype isRectNullptr;
133 if (!OnMakeDrawingRect(env, argv[ARGC_TWO], drawingRect, isRectNullptr)) {
134 ROSEN_LOGE("JsTextBlob::MakeFromRunBuffer Argv[2] is invalid");
135 return nullptr;
136 }
137 runBuffer = textBlobBuilder->AllocRunPos(*font, size, isRectNullptr == napi_null ? nullptr : &drawingRect);
138 }
139 if (!OnMakeRunBuffer(env, runBuffer, size, array)) {
140 ROSEN_LOGE("JsTextBlob::MakeFromRunBuffer Argv[0] is invalid");
141 return nullptr;
142 }
143
144 std::shared_ptr<TextBlob> textBlob = textBlobBuilder->Make();
145 if (textBlob == nullptr) {
146 ROSEN_LOGE("JsTextBlob::MakeFromRunBuffer textBlob is nullptr");
147 return nullptr;
148 }
149 return JsTextBlob::CreateJsTextBlob(env, textBlob);
150 }
151
OnMakeDrawingRect(napi_env & env,napi_value & argv,Rect & drawingRect,napi_valuetype & isRectNullptr)152 bool JsTextBlob::OnMakeDrawingRect(napi_env& env, napi_value& argv, Rect& drawingRect, napi_valuetype& isRectNullptr)
153 {
154 napi_typeof(env, argv, &isRectNullptr);
155 if (isRectNullptr != napi_null) {
156 napi_value tempValue = nullptr;
157 double left = 0.0;
158 double top = 0.0;
159 double right = 0.0;
160 double bottom = 0.0;
161 napi_get_named_property(env, argv, "left", &tempValue);
162 bool isLeftOk = ConvertFromJsValue(env, tempValue, left);
163 napi_get_named_property(env, argv, "right", &tempValue);
164 bool isRightOk = ConvertFromJsValue(env, tempValue, right);
165 napi_get_named_property(env, argv, "top", &tempValue);
166 bool isTopOk = ConvertFromJsValue(env, tempValue, top);
167 napi_get_named_property(env, argv, "bottom", &tempValue);
168 bool isBottomOk = ConvertFromJsValue(env, tempValue, bottom);
169 if (!(isLeftOk && isRightOk && isTopOk && isBottomOk)) {
170 return false;
171 }
172
173 drawingRect.SetLeft(left);
174 drawingRect.SetRight(right);
175 drawingRect.SetTop(top);
176 drawingRect.SetBottom(bottom);
177 }
178 return true;
179 }
180
OnMakeRunBuffer(napi_env & env,TextBlobBuilder::RunBuffer & runBuffer,uint32_t size,napi_value & array)181 bool JsTextBlob::OnMakeRunBuffer(napi_env& env, TextBlobBuilder::RunBuffer& runBuffer, uint32_t size, napi_value& array)
182 {
183 for (uint32_t i = 0; i < size; i++) {
184 napi_value tempRunBuffer = nullptr;
185 napi_get_element(env, array, i, &tempRunBuffer);
186 napi_value tempValue = nullptr;
187 uint32_t glyph = 0;
188 double positionX = 0.0;
189 double positionY = 0.0;
190 napi_get_named_property(env, tempRunBuffer, "glyph", &tempValue);
191 bool isGlyphOk = ConvertFromJsValue(env, tempValue, glyph);
192 napi_get_named_property(env, tempRunBuffer, "positionX", &tempValue);
193 bool isPositionXOk = ConvertFromJsValue(env, tempValue, positionX);
194 napi_get_named_property(env, tempRunBuffer, "positionY", &tempValue);
195 bool isPositionYOk = ConvertFromJsValue(env, tempValue, positionY);
196 if (!(isGlyphOk && isPositionXOk && isPositionYOk)) {
197 return false;
198 }
199
200 runBuffer.glyphs[i] = (uint16_t)glyph;
201 runBuffer.pos[2 * i] = positionX; // 2: double
202 runBuffer.pos[2 * i + 1] = positionY; // 2: double
203 }
204 return true;
205 }
206
MakeFromString(napi_env env,napi_callback_info info)207 napi_value JsTextBlob::MakeFromString(napi_env env, napi_callback_info info)
208 {
209 size_t argc = ARGC_THREE;
210 napi_value argv[ARGC_THREE] = {nullptr};
211 napi_status status = napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
212 if (status != napi_ok || argc < ARGC_TWO || argc > ARGC_THREE) {
213 ROSEN_LOGE("JsTextBlob::MakeFromString Argc is invalid: %{public}zu", argc);
214 return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
215 }
216
217 std::string text = "";
218 if (!ConvertFromJsValue(env, argv[0], text)) {
219 ROSEN_LOGE("JsTextBlob::MakeFromString Argv[0] is invalid");
220 return nullptr;
221 }
222
223 void* pointerResult = nullptr;
224 napi_unwrap(env, argv[1], &pointerResult);
225 auto jsFont = static_cast<JsFont*>(pointerResult);
226 if (jsFont == nullptr) {
227 ROSEN_LOGE("JsTextBlob::MakeFromString jsFont is nullptr");
228 return nullptr;
229 }
230 std::shared_ptr<Font> font = jsFont->GetFont();
231 if (font == nullptr) {
232 ROSEN_LOGE("JsTextBlob::MakeFromString font is nullptr");
233 return nullptr;
234 }
235
236 std::shared_ptr<TextBlob> textBlob;
237 if (argc == ARGC_TWO) {
238 textBlob = TextBlob::MakeFromString(text.c_str(), *font);
239 } else {
240 TextEncoding TextEncoding = TextEncoding::UTF8;
241 if (!ConvertFromJsTextEncoding(env, TextEncoding, argv[ARGC_TWO])) {
242 ROSEN_LOGE("JsTextBlob::MakeFromString Argv[2] is invalid");
243 return nullptr;
244 }
245 textBlob = TextBlob::MakeFromString(text.c_str(), *font, TextEncoding);
246 }
247
248 if (textBlob == nullptr) {
249 ROSEN_LOGE("JsTextBlob::MakeFromString textBlob is nullptr");
250 return nullptr;
251 }
252 napi_value jsTextBlob = JsTextBlob::CreateJsTextBlob(env, textBlob);
253 if (jsTextBlob == nullptr) {
254 ROSEN_LOGE("JsTextBlob::MakeFromString jsTextBlob is nullptr");
255 return nullptr;
256 }
257 return jsTextBlob;
258 }
259
CreateJsTextBlob(napi_env env,const std::shared_ptr<TextBlob> textBlob)260 napi_value JsTextBlob::CreateJsTextBlob(napi_env env, const std::shared_ptr<TextBlob> textBlob)
261 {
262 napi_value constructor = nullptr;
263 napi_value result = nullptr;
264 napi_status status = napi_get_reference_value(env, constructor_, &constructor);
265 if (status == napi_ok) {
266 drawingTextBlob = textBlob;
267 status = napi_new_instance(env, constructor, 0, nullptr, &result);
268 if (status == napi_ok) {
269 return result;
270 } else {
271 ROSEN_LOGE("JsTextBlob::CreateJsTextBlob New instance could not be obtained");
272 }
273 }
274 return result;
275 }
276
Bounds(napi_env env,napi_callback_info info)277 napi_value JsTextBlob::Bounds(napi_env env, napi_callback_info info)
278 {
279 JsTextBlob* me = CheckParamsAndGetThis<JsTextBlob>(env, info);
280 return (me != nullptr) ? me->OnBounds(env, info) : nullptr;
281 }
282
OnBounds(napi_env env,napi_callback_info info)283 napi_value JsTextBlob::OnBounds(napi_env env, napi_callback_info info)
284 {
285 if (m_textBlob == nullptr) {
286 ROSEN_LOGE("JsTextBlob::OnBounds textBlob is nullptr");
287 return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
288 }
289 std::shared_ptr<Rect> rect = m_textBlob->Bounds();
290
291 if (!rect) {
292 ROSEN_LOGE("JsTextBlob::OnBounds rect is nullptr");
293 return nullptr;
294 }
295 return GetRectAndConvertToJsValue(env, rect);
296 }
297
GetTextBlob()298 std::shared_ptr<TextBlob> JsTextBlob::GetTextBlob()
299 {
300 return m_textBlob;
301 }
302 } // namespace Drawing
303 } // namespace OHOS::Rosen
304