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