• 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 "js_text_blob.h"
17 
18 #include "native_value.h"
19 
20 #include "js_drawing_utils.h"
21 #include "font_napi/js_font.h"
22 
23 namespace OHOS::Rosen {
24 namespace Drawing {
25 const std::string CLASS_NAME = "TextBlob";
26 thread_local napi_ref JsTextBlob::constructor_ = nullptr;
27 std::shared_ptr<TextBlob> drawingTextBlob;
28 static constexpr size_t CHAR16_SIZE = 2;
Init(napi_env env,napi_value exportObj)29 napi_value JsTextBlob::Init(napi_env env, napi_value exportObj)
30 {
31     napi_property_descriptor properties[] = {
32         DECLARE_NAPI_FUNCTION("bounds", JsTextBlob::Bounds),
33         DECLARE_NAPI_STATIC_FUNCTION("makeFromString", JsTextBlob::MakeFromString),
34         DECLARE_NAPI_STATIC_FUNCTION("makeFromRunBuffer", JsTextBlob::MakeFromRunBuffer),
35         DECLARE_NAPI_STATIC_FUNCTION("makeFromPosText", JsTextBlob::MakeFromPosText),
36         DECLARE_NAPI_FUNCTION("uniqueID", JsTextBlob::UniqueID),
37     };
38 
39     napi_value constructor = nullptr;
40     napi_status status = napi_define_class(env, CLASS_NAME.c_str(), NAPI_AUTO_LENGTH, Constructor, nullptr,
41                                            sizeof(properties) / sizeof(properties[0]), properties, &constructor);
42     if (status != napi_ok) {
43         ROSEN_LOGE("Drawing_napi: Failed to define TextBlob class");
44         return nullptr;
45     }
46 
47     status = napi_create_reference(env, constructor, 1, &constructor_);
48     if (status != napi_ok) {
49         ROSEN_LOGE("Drawing_napi: Failed to create reference of constructor");
50         return nullptr;
51     }
52 
53     status = napi_set_named_property(env, exportObj, CLASS_NAME.c_str(), constructor);
54     if (status != napi_ok) {
55         ROSEN_LOGE("Drawing_napi: Failed to set constructor");
56         return nullptr;
57     }
58 
59     status = napi_define_properties(env, exportObj, sizeof(properties) / sizeof(properties[0]), properties);
60     if (status != napi_ok) {
61         ROSEN_LOGE("Drawing_napi: Failed to define static function");
62         return nullptr;
63     }
64     return exportObj;
65 }
66 
~JsTextBlob()67 JsTextBlob::~JsTextBlob()
68 {
69     m_textBlob = nullptr;
70 }
71 
Constructor(napi_env env,napi_callback_info info)72 napi_value JsTextBlob::Constructor(napi_env env, napi_callback_info info)
73 {
74     size_t argCount = 0;
75     napi_value jsThis = nullptr;
76     napi_status status = napi_get_cb_info(env, info, &argCount, nullptr, &jsThis, nullptr);
77     if (status != napi_ok) {
78         ROSEN_LOGE("failed to napi_get_cb_info");
79         return nullptr;
80     }
81 
82     JsTextBlob *jsTextBlob = new JsTextBlob(env, drawingTextBlob);
83 
84     status = napi_wrap(env, jsThis, jsTextBlob,
85                        JsTextBlob::Destructor, nullptr, nullptr);
86     if (status != napi_ok) {
87         delete jsTextBlob;
88         ROSEN_LOGE("Failed to wrap native instance");
89         return nullptr;
90     }
91     return jsThis;
92 }
93 
Destructor(napi_env env,void * nativeObject,void * finalize)94 void JsTextBlob::Destructor(napi_env env, void *nativeObject, void *finalize)
95 {
96     (void)finalize;
97     if (nativeObject != nullptr) {
98         JsTextBlob *napi = reinterpret_cast<JsTextBlob *>(nativeObject);
99         delete napi;
100     }
101 }
102 
MakeFromRunBuffer(napi_env env,napi_callback_info info)103 napi_value JsTextBlob::MakeFromRunBuffer(napi_env env, napi_callback_info info)
104 {
105     size_t argc = ARGC_THREE;
106     napi_value argv[ARGC_THREE] = {nullptr};
107     CHECK_PARAM_NUMBER_WITH_OPTIONAL_PARAMS(argv, argc, ARGC_TWO, ARGC_THREE);
108 
109     napi_value array = argv[ARGC_ZERO];
110     uint32_t size = 0;
111     if (napi_get_array_length(env, array, &size) != napi_ok) {
112         return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, "Incorrect parameter0 type.");
113     }
114 
115     JsFont* jsFont = nullptr;
116     GET_UNWRAP_PARAM(ARGC_ONE, jsFont);
117 
118     std::shared_ptr<Font> font = jsFont->GetFont();
119     if (font == nullptr) {
120         ROSEN_LOGE("JsTextBlob::MakeFromRunBuffer font is nullptr");
121         return nullptr;
122     }
123     std::shared_ptr<Font> themeFont = GetThemeFont(font);
124     if (themeFont != nullptr) {
125         font = themeFont;
126     }
127 
128     TextBlobBuilder::RunBuffer runBuffer;
129     std::shared_ptr<TextBlobBuilder> textBlobBuilder = std::make_shared<TextBlobBuilder>();
130     if (argc == ARGC_TWO) {
131         runBuffer = textBlobBuilder->AllocRunPos(*font, size);
132     } else {
133         Rect drawingRect;
134         napi_valuetype isRectNullptr;
135         if (!OnMakeDrawingRect(env, argv[ARGC_TWO], drawingRect, isRectNullptr)) {
136             ROSEN_LOGE("JsTextBlob::MakeFromRunBuffer Argv[2] is invalid");
137             return nullptr;
138         }
139         runBuffer = textBlobBuilder->AllocRunPos(*font, size, isRectNullptr == napi_null ? nullptr : &drawingRect);
140     }
141     if (!OnMakeRunBuffer(env, runBuffer, size, array)) {
142         ROSEN_LOGE("JsTextBlob::MakeFromRunBuffer Argv[0] is invalid");
143         return nullptr;
144     }
145 
146     std::shared_ptr<TextBlob> textBlob = textBlobBuilder->Make();
147     if (textBlob == nullptr) {
148         ROSEN_LOGE("JsTextBlob::MakeFromRunBuffer textBlob is nullptr");
149         return nullptr;
150     }
151     return JsTextBlob::CreateJsTextBlob(env, textBlob);
152 }
153 
OnMakeDrawingRect(napi_env & env,napi_value & argv,Rect & drawingRect,napi_valuetype & isRectNullptr)154 bool JsTextBlob::OnMakeDrawingRect(napi_env& env, napi_value& argv, Rect& drawingRect, napi_valuetype& isRectNullptr)
155 {
156     napi_typeof(env, argv, &isRectNullptr);
157     if (isRectNullptr != napi_null) {
158         double ltrb[ARGC_FOUR] = {0};
159         if (!ConvertFromJsRect(env, argv, ltrb, ARGC_FOUR)) {
160             return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM,
161                 "Incorrect parameter2 type. The type of left, top, right and bottom must be number.");
162         }
163 
164         drawingRect.SetLeft(ltrb[ARGC_ZERO]);
165         drawingRect.SetTop(ltrb[ARGC_ONE]);
166         drawingRect.SetRight(ltrb[ARGC_TWO]);
167         drawingRect.SetBottom(ltrb[ARGC_THREE]);
168     }
169     return true;
170 }
171 
OnMakeRunBuffer(napi_env & env,TextBlobBuilder::RunBuffer & runBuffer,uint32_t size,napi_value & array)172 bool JsTextBlob::OnMakeRunBuffer(napi_env& env, TextBlobBuilder::RunBuffer& runBuffer, uint32_t size, napi_value& array)
173 {
174     if (size > MAX_ELEMENTSIZE) {
175         ROSEN_LOGE("JsTextBlob::OnMakeRunBuffer size exceeds the upper limit");
176         return false;
177     }
178     for (uint32_t i = 0; i < size; i++) {
179         napi_value tempRunBuffer = nullptr;
180         napi_get_element(env, array, i, &tempRunBuffer);
181         napi_value tempValue = nullptr;
182         uint32_t glyph = 0;
183         double positionX = 0.0;
184         double positionY = 0.0;
185         napi_get_named_property(env, tempRunBuffer, "glyph", &tempValue);
186         bool isGlyphOk = ConvertFromJsValue(env, tempValue, glyph);
187         napi_get_named_property(env, tempRunBuffer, "positionX", &tempValue);
188         bool isPositionXOk = ConvertFromJsValue(env, tempValue, positionX);
189         napi_get_named_property(env, tempRunBuffer, "positionY", &tempValue);
190         bool isPositionYOk = ConvertFromJsValue(env, tempValue, positionY);
191         if (!(isGlyphOk && isPositionXOk && isPositionYOk)) {
192             return false;
193         }
194 
195         runBuffer.glyphs[i] = (uint16_t)glyph;
196         runBuffer.pos[2 * i] = positionX; // 2: double
197         runBuffer.pos[2 * i + 1] = positionY; // 2: double
198     }
199     return true;
200 }
201 
MakeFromString(napi_env env,napi_callback_info info)202 napi_value JsTextBlob::MakeFromString(napi_env env, napi_callback_info info)
203 {
204     size_t argc = ARGC_THREE;
205     napi_value argv[ARGC_THREE] = {nullptr};
206     CHECK_PARAM_NUMBER_WITH_OPTIONAL_PARAMS(argv, argc, ARGC_TWO, ARGC_THREE);
207 
208     JsFont* jsFont = nullptr;
209     GET_UNWRAP_PARAM(ARGC_ONE, jsFont);
210 
211     std::shared_ptr<Font> font = jsFont->GetFont();
212     if (font == nullptr) {
213         ROSEN_LOGE("JsTextBlob::MakeFromString font is nullptr");
214         return nullptr;
215     }
216     std::shared_ptr<Font> themeFont = GetThemeFont(font);
217     if (themeFont != nullptr) {
218         font = themeFont;
219     }
220 
221     // Chinese characters need to be encoded with UTF16
222     size_t len = 0;
223     if (napi_get_value_string_utf16(env, argv[ARGC_ZERO], nullptr, 0, &len) != napi_ok) {
224         return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, "Incorrect parameter0 type.");
225     }
226     char16_t* buffer = new(std::nothrow) char16_t[len + 1];
227     if (!buffer) {
228         ROSEN_LOGE("JsTextBlob::MakeFromString fail to create buffer");
229         return nullptr;
230     }
231     if (napi_get_value_string_utf16(env, argv[ARGC_ZERO], buffer, len + 1, &len) != napi_ok) {
232         delete[] buffer;
233         return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, "Incorrect parameter0 type.");
234     }
235     std::shared_ptr<TextBlob> textBlob = TextBlob::MakeFromText(buffer, CHAR16_SIZE * len, *font, TextEncoding::UTF16);
236 
237     if (textBlob == nullptr) {
238         delete[] buffer;
239         ROSEN_LOGE("JsTextBlob::MakeFromString textBlob is nullptr");
240         return nullptr;
241     }
242     napi_value jsTextBlob = JsTextBlob::CreateJsTextBlob(env, textBlob);
243     if (jsTextBlob == nullptr) {
244         delete[] buffer;
245         ROSEN_LOGE("JsTextBlob::MakeFromString jsTextBlob is nullptr");
246         return nullptr;
247     }
248     delete[] buffer;
249     return jsTextBlob;
250 }
251 
UniqueID(napi_env env,napi_callback_info info)252 napi_value JsTextBlob::UniqueID(napi_env env, napi_callback_info info)
253 {
254     JsTextBlob* me = CheckParamsAndGetThis<JsTextBlob>(env, info);
255     return (me != nullptr) ? me->OnUniqueID(env, info) : nullptr;
256 }
257 
OnUniqueID(napi_env env,napi_callback_info info)258 napi_value JsTextBlob::OnUniqueID(napi_env env, napi_callback_info info)
259 {
260     if (m_textBlob == nullptr) {
261         ROSEN_LOGE("JsTextBlob::OnUniqueID textBlob is nullptr");
262         return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
263     }
264     return CreateJsNumber(env, m_textBlob->UniqueID());
265 }
266 
MakePoints(napi_env & env,Point * point,uint32_t size,napi_value & array)267 static bool MakePoints(napi_env& env, Point* point, uint32_t size, napi_value& array)
268 {
269     napi_status status = napi_invalid_arg;
270     for (uint32_t i = 0; i < size; i++) {
271         napi_value tempNumber = nullptr;
272         status = napi_get_element(env, array, i, &tempNumber);
273         if (status != napi_ok) {
274             return false;
275         }
276         napi_value tempValue = nullptr;
277         status = napi_get_named_property(env, tempNumber, "x", &tempValue);
278         if (status != napi_ok) {
279             return false;
280         }
281         double pointX = 0.0;
282         const bool isPointXOk = ConvertFromJsValue(env, tempValue, pointX);
283         status = napi_get_named_property(env, tempNumber, "y", &tempValue);
284         if (status != napi_ok) {
285             return false;
286         }
287         double pointY = 0.0;
288         const bool isPointYOk = ConvertFromJsValue(env, tempValue, pointY);
289         if (!(isPointXOk && isPointYOk)) {
290             return false;
291         }
292         point[i] = Point(pointX, pointY);
293     }
294     return true;
295 }
296 
getJsTextBlob(const char * buffer,size_t bufferLen,const Point points[],const std::shared_ptr<Font> & font,napi_env env)297 static napi_value getJsTextBlob(const char* buffer, size_t bufferLen, const Point points[],
298                                 const std::shared_ptr<Font>& font, napi_env env)
299 {
300     std::shared_ptr<TextBlob> textBlob =
301         TextBlob::MakeFromPosText(buffer, bufferLen, points, *font, TextEncoding::UTF8);
302     if (textBlob == nullptr) {
303         ROSEN_LOGE("getJsTextBlob: textBlob is nullptr");
304         return nullptr;
305     }
306     napi_value jsTextBlob = JsTextBlob::CreateJsTextBlob(env, textBlob);
307     if (jsTextBlob == nullptr) {
308         ROSEN_LOGE("getJsTextBlob: jsTextBlob is nullptr");
309         return nullptr;
310     }
311     return jsTextBlob;
312 }
313 
MakeFromPosText(napi_env env,napi_callback_info info)314 napi_value JsTextBlob::MakeFromPosText(napi_env env, napi_callback_info info)
315 {
316     napi_value argv[ARGC_FOUR] = {nullptr};
317     CHECK_PARAM_NUMBER_WITHOUT_OPTIONAL_PARAMS(argv, ARGC_FOUR);
318 
319     uint32_t len = 0;
320     GET_UINT32_PARAM(ARGC_ONE, len);
321 
322     size_t bufferLen = 0;
323     if (napi_get_value_string_utf8(env, argv[ARGC_ZERO], nullptr, 0, &bufferLen) != napi_ok) {
324         return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, "Incorrect parameter0 type.");
325     }
326 
327     char* buffer = new(std::nothrow) char[bufferLen + 1];
328     if (!buffer) {
329         ROSEN_LOGE("JsTextBlob::MakeFromPosText: failed to create buffer");
330         return nullptr;
331     }
332     if (napi_get_value_string_utf8(env, argv[ARGC_ZERO], buffer, bufferLen + 1, &bufferLen) != napi_ok) {
333         delete[] buffer;
334         return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, "Incorrect Argv[0] type.");
335     }
336 
337     napi_value array = argv[ARGC_TWO];
338     uint32_t pointsSize = 0;
339     if (napi_get_array_length(env, array, &pointsSize) != napi_ok) {
340         delete[] buffer;
341         return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, "Incorrect Argv[2].");
342     }
343     if (pointsSize == 0 || bufferLen == 0) {
344         delete[] buffer;
345         return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, "Argv[0] is empty.");
346     }
347     if (len != pointsSize) {
348         delete[] buffer;
349         return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM,
350             "string length does not match points array length.");
351     }
352 
353     JsFont* jsFont = nullptr;
354     if ((napi_unwrap(env, argv[ARGC_THREE], reinterpret_cast<void**>(&jsFont)) != napi_ok) || jsFont == nullptr) {
355         delete[] buffer;
356         return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM,
357             std::string("Incorrect ") + __FUNCTION__ + " parameter" + std::to_string(ARGC_THREE) + " type.");
358     }
359     std::shared_ptr<Font> font = jsFont->GetFont();
360     if (font == nullptr) {
361         delete[] buffer;
362         ROSEN_LOGE("JsTextBlob::MakeFromPosText: font is nullptr");
363         return nullptr;
364     }
365     std::shared_ptr<Font> themeFont = GetThemeFont(font);
366     if (themeFont != nullptr) {
367         font = themeFont;
368     }
369 
370     Point* points = new(std::nothrow) Point[pointsSize];
371     if (!points) {
372         delete[] buffer;
373         ROSEN_LOGE("JsTextBlob::MakeFromPosText: failed to create Point");
374         return nullptr;
375     }
376     if (!MakePoints(env, points, pointsSize, array)) {
377         delete[] buffer;
378         delete[] points;
379         ROSEN_LOGE("JsTextBlob::MakeFromPosText: Argv[2] is invalid");
380         return nullptr;
381     }
382     napi_value jsTextBlob = getJsTextBlob(buffer, bufferLen, points, font, env);
383     delete[] buffer;
384     delete[] points;
385     return jsTextBlob;
386 }
387 
CreateJsTextBlob(napi_env env,const std::shared_ptr<TextBlob> textBlob)388 napi_value JsTextBlob::CreateJsTextBlob(napi_env env, const std::shared_ptr<TextBlob> textBlob)
389 {
390     napi_value constructor = nullptr;
391     napi_value result = nullptr;
392     napi_status status = napi_get_reference_value(env, constructor_, &constructor);
393     if (status == napi_ok) {
394         drawingTextBlob = textBlob;
395         status = napi_new_instance(env, constructor, 0, nullptr, &result);
396         if (status == napi_ok) {
397             return result;
398         } else {
399             ROSEN_LOGE("JsTextBlob::CreateJsTextBlob New instance could not be obtained");
400         }
401     }
402     return result;
403 }
404 
Bounds(napi_env env,napi_callback_info info)405 napi_value JsTextBlob::Bounds(napi_env env, napi_callback_info info)
406 {
407     JsTextBlob* me = CheckParamsAndGetThis<JsTextBlob>(env, info);
408     return (me != nullptr) ? me->OnBounds(env, info) : nullptr;
409 }
410 
OnBounds(napi_env env,napi_callback_info info)411 napi_value JsTextBlob::OnBounds(napi_env env, napi_callback_info info)
412 {
413     if (m_textBlob == nullptr) {
414         ROSEN_LOGE("JsTextBlob::OnBounds textBlob is nullptr");
415         return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
416     }
417     std::shared_ptr<Rect> rect = m_textBlob->Bounds();
418 
419     if (!rect) {
420         ROSEN_LOGE("JsTextBlob::OnBounds rect is nullptr");
421         return nullptr;
422     }
423     return GetRectAndConvertToJsValue(env, *rect);
424 }
425 
GetTextBlob()426 std::shared_ptr<TextBlob> JsTextBlob::GetTextBlob()
427 {
428     return m_textBlob;
429 }
430 } // namespace Drawing
431 } // namespace OHOS::Rosen
432