1 /*
2 * Copyright (c) 2022-2023 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 "core/components_ng/render/adapter/txt_paragraph.h"
17
18 #include "base/log/ace_performance_monitor.h"
19 #include "core/components/font/constants_converter.h"
20 #include "core/components_ng/render/adapter/pixelmap_image.h"
21 #include "core/components_ng/render/adapter/txt_font_collection.h"
22 #include "core/components_ng/render/drawing_prop_convertor.h"
23
24 namespace OHOS::Ace::NG {
25 namespace {
26 const std::u16string ELLIPSIS = u"\u2026";
27 const std::u16string SYMBOL_TRANS = u"\uF0001";
28 const int32_t LENGTH_INCREMENT = 2;
29 constexpr char16_t NEWLINE_CODE = u'\n';
30 constexpr float TEXT_SPLIT_RATIO = 0.6f;
31 } // namespace
Create(const ParagraphStyle & paraStyle,const RefPtr<FontCollection> & fontCollection)32 RefPtr<Paragraph> Paragraph::Create(const ParagraphStyle& paraStyle, const RefPtr<FontCollection>& fontCollection)
33 {
34 auto txtFontCollection = DynamicCast<TxtFontCollection>(fontCollection);
35 CHECK_NULL_RETURN(txtFontCollection, nullptr);
36 auto sharedFontCollection = txtFontCollection->GetRawFontCollection();
37 return AceType::MakeRefPtr<TxtParagraph>(paraStyle, sharedFontCollection);
38 }
39
Create(void * rsParagraph)40 RefPtr<Paragraph> Paragraph::Create(void* rsParagraph)
41 {
42 return AceType::MakeRefPtr<TxtParagraph>(rsParagraph);
43 }
44
IsValid()45 bool TxtParagraph::IsValid()
46 {
47 return GetParagraph() != nullptr;
48 }
49
CreateBuilder()50 void TxtParagraph::CreateBuilder()
51 {
52 ACE_TEXT_SCOPED_TRACE("TxtParagraph::CreateBuilder");
53 CHECK_NULL_VOID(!hasExternalParagraph_);
54 placeholderPosition_.clear();
55 #ifndef USE_GRAPHIC_TEXT_GINE
56 txt::ParagraphStyle style;
57 style.text_direction = Constants::ConvertTxtTextDirection(paraStyle_.direction);
58 style.text_align = Constants::ConvertTxtTextAlign(paraStyle_.align);
59 style.max_lines = paraStyle_.maxLines;
60 style.font_size = paraStyle_.fontSize; // libtxt style.font_size
61 style.word_break_type = static_cast<minikin::WordBreakType>(paraStyle_.wordBreak);
62 #else
63 Rosen::TypographyStyle style;
64 style.textDirection = Constants::ConvertTxtTextDirection(paraStyle_.direction);
65 style.textAlign = Constants::ConvertTxtTextAlign(paraStyle_.align);
66 style.maxLines = paraStyle_.maxLines == UINT32_MAX ? UINT32_MAX - 1 : paraStyle_.maxLines;
67 style.fontSize = paraStyle_.fontSize; // Rosen style.fontSize
68 style.wordBreakType = static_cast<Rosen::WordBreakType>(paraStyle_.wordBreak);
69 style.ellipsisModal = static_cast<Rosen::EllipsisModal>(paraStyle_.ellipsisMode);
70 style.textSplitRatio = TEXT_SPLIT_RATIO;
71 style.breakStrategy = static_cast<Rosen::BreakStrategy>(paraStyle_.lineBreakStrategy);
72 #endif
73 style.locale = paraStyle_.fontLocale;
74 if (paraStyle_.textOverflow == TextOverflow::ELLIPSIS) {
75 style.ellipsis = ELLIPSIS;
76 }
77 #if !defined(FLUTTER_2_5) && !defined(NEW_SKIA)
78 // keep WordBreak define same with WordBreakType in minikin
79 #ifndef USE_GRAPHIC_TEXT_GINE
80 style.word_break_type = static_cast<minikin::WordBreakType>(paraStyle_.wordBreak);
81 #else
82 style.wordBreakType = static_cast<Rosen::WordBreakType>(paraStyle_.wordBreak);
83 style.breakStrategy = static_cast<Rosen::BreakStrategy>(paraStyle_.lineBreakStrategy);
84 #endif
85 #endif
86 #ifndef USE_GRAPHIC_TEXT_GINE
87 builder_ = txt::ParagraphBuilder::CreateTxtBuilder(style, fontCollection_);
88 #else
89 builder_ = Rosen::TypographyCreate::Create(style, fontCollection_);
90 #endif
91 }
92
PushStyle(const TextStyle & style)93 void TxtParagraph::PushStyle(const TextStyle& style)
94 {
95 ACE_TEXT_SCOPED_TRACE("TxtParagraph::PushStyle");
96 CHECK_NULL_VOID(!hasExternalParagraph_);
97 if (!builder_) {
98 CreateBuilder();
99 }
100
101 #ifndef USE_GRAPHIC_TEXT_GINE
102 txt::TextStyle txtStyle;
103 #else
104 Rosen::TextStyle txtStyle;
105 #endif
106 textAlign_ = style.GetTextAlign();
107 Constants::ConvertTxtStyle(style, PipelineContext::GetCurrentContextSafely(), txtStyle);
108 builder_->PushStyle(txtStyle);
109 }
110
PopStyle()111 void TxtParagraph::PopStyle()
112 {
113 ACE_TEXT_SCOPED_TRACE("TxtParagraph::PopStyle");
114 CHECK_NULL_VOID(!hasExternalParagraph_ && builder_);
115 #ifndef USE_GRAPHIC_TEXT_GINE
116 builder_->Pop();
117 #else
118 builder_->PopStyle();
119 #endif
120 }
121
AddText(const std::u16string & text)122 void TxtParagraph::AddText(const std::u16string& text)
123 {
124 ACE_TEXT_SCOPED_TRACE("TxtParagraph::AddText");
125 if (!builder_) {
126 CreateBuilder();
127 }
128 text_ += text;
129 CHECK_NULL_VOID(!hasExternalParagraph_);
130 #ifndef USE_GRAPHIC_TEXT_GINE
131 builder_->AddText(text);
132 #else
133 builder_->AppendText(text);
134 #endif
135 }
136
AddSymbol(const std::uint32_t & symbolId)137 void TxtParagraph::AddSymbol(const std::uint32_t& symbolId)
138 {
139 ACE_TEXT_SCOPED_TRACE("TxtParagraph::AddSymbol");
140 CHECK_NULL_VOID(!hasExternalParagraph_);
141 if (!builder_) {
142 CreateBuilder();
143 }
144 text_ += SYMBOL_TRANS;
145 builder_->AppendSymbol(symbolId);
146 }
147
AddPlaceholder(const PlaceholderRun & span)148 int32_t TxtParagraph::AddPlaceholder(const PlaceholderRun& span)
149 {
150 ACE_TEXT_SCOPED_TRACE("TxtParagraph::AddPlaceholder");
151 CHECK_NULL_RETURN(!hasExternalParagraph_, 0);
152 if (!builder_) {
153 CreateBuilder();
154 }
155 #ifndef USE_GRAPHIC_TEXT_GINE
156 txt::PlaceholderRun txtSpan;
157 #else
158 OHOS::Rosen::PlaceholderSpan txtSpan;
159 #endif
160 Constants::ConvertPlaceholderRun(span, txtSpan);
161 #ifndef USE_GRAPHIC_TEXT_GINE
162 builder_->AddPlaceholder(txtSpan);
163 #else
164 builder_->AppendPlaceholder(txtSpan);
165 #endif
166 auto position = static_cast<size_t>(placeholderCnt_) + text_.length();
167 placeholderPosition_.emplace_back(position);
168 return placeholderCnt_++;
169 }
170
Build()171 void TxtParagraph::Build()
172 {
173 OTHER_DURATION();
174 ACE_TEXT_SCOPED_TRACE("TxtParagraph::Build");
175 CHECK_NULL_VOID(!hasExternalParagraph_ && builder_);
176 #ifndef USE_GRAPHIC_TEXT_GINE
177 paragraph_ = builder_->Build();
178 #else
179 paragraph_ = builder_->CreateTypography();
180 #endif
181 }
182
183 uint32_t TxtParagraph::destructCount = 0;
184
~TxtParagraph()185 TxtParagraph::~TxtParagraph()
186 {
187 if (destructCount % 100 == 0) {
188 TAG_LOGI(AceLogTag::ACE_TEXT_FIELD,
189 "destroy TxtParagraph with placeholderCnt_ %{public}d, textAlign_ %{public}d, count %{public}u",
190 placeholderCnt_, static_cast<int>(textAlign_), destructCount);
191 }
192 destructCount++;
193 }
194
Reset()195 void TxtParagraph::Reset()
196 {
197 paragraph_.reset();
198 builder_.reset();
199 fontCollection_.reset();
200 }
201
Layout(float width)202 void TxtParagraph::Layout(float width)
203 {
204 OTHER_DURATION();
205 ACE_TEXT_SCOPED_TRACE("TxtParagraph::Layout");
206 CHECK_NULL_VOID(!hasExternalParagraph_ && paragraph_);
207 paragraph_->Layout(width);
208 }
209
GetHeight()210 float TxtParagraph::GetHeight()
211 {
212 auto paragrah = GetParagraph();
213 CHECK_NULL_RETURN(paragrah, 0.0f);
214 return static_cast<float>(paragrah->GetHeight());
215 }
216
GetTextWidth()217 float TxtParagraph::GetTextWidth()
218 {
219 auto paragrah = GetParagraph();
220 CHECK_NULL_RETURN(paragrah, 0.0f);
221 if (GetLineCount() == 1) {
222 #ifndef USE_GRAPHIC_TEXT_GINE
223 return std::max(paragrah->GetLongestLine(), paragrah->GetMaxIntrinsicWidth());
224 #else
225 return std::max(paragrah->GetLongestLineWithIndent(), paragrah->GetMaxIntrinsicWidth());
226 #endif
227 }
228 #ifndef USE_GRAPHIC_TEXT_GINE
229 return paragrah->GetLongestLine();
230 #else
231 return paragrah->GetLongestLineWithIndent();
232 #endif
233 }
234
GetMaxIntrinsicWidth()235 float TxtParagraph::GetMaxIntrinsicWidth()
236 {
237 auto paragrah = GetParagraph();
238 CHECK_NULL_RETURN(paragrah, 0.0f);
239 return static_cast<float>(paragrah->GetMaxIntrinsicWidth());
240 }
241
DidExceedMaxLines()242 bool TxtParagraph::DidExceedMaxLines()
243 {
244 auto paragrah = GetParagraph();
245 CHECK_NULL_RETURN(paragrah, false);
246 return paragrah->DidExceedMaxLines();
247 }
248
GetLongestLine()249 float TxtParagraph::GetLongestLine()
250 {
251 auto paragrah = GetParagraph();
252 CHECK_NULL_RETURN(paragrah, 0.0f);
253 #ifndef USE_GRAPHIC_TEXT_GINE
254 return static_cast<float>(paragrah->GetLongestLine());
255 #else
256 return static_cast<float>(paragrah->GetActualWidth());
257 #endif
258 }
259
GetLongestLineWithIndent()260 float TxtParagraph::GetLongestLineWithIndent()
261 {
262 auto paragrah = GetParagraph();
263 CHECK_NULL_RETURN(paragrah, 0.0f);
264 return static_cast<float>(paragrah->GetLongestLineWithIndent());
265 }
266
GetMaxWidth()267 float TxtParagraph::GetMaxWidth()
268 {
269 auto paragrah = GetParagraph();
270 CHECK_NULL_RETURN(paragrah, 0.0f);
271 return static_cast<float>(paragrah->GetMaxWidth());
272 }
273
GetAlphabeticBaseline()274 float TxtParagraph::GetAlphabeticBaseline()
275 {
276 auto paragrah = GetParagraph();
277 CHECK_NULL_RETURN(paragrah, 0.0f);
278 return static_cast<float>(paragrah->GetAlphabeticBaseline());
279 }
280
GetLineCount()281 size_t TxtParagraph::GetLineCount()
282 {
283 #ifndef USE_GRAPHIC_TEXT_GINE
284 auto* paragraphTxt = static_cast<txt::ParagraphTxt*>(GetParagraph());
285 CHECK_NULL_RETURN(paragraphTxt, 0);
286 return paragraphTxt->GetLineCount();
287 #else
288 auto paragrah = GetParagraph();
289 CHECK_NULL_RETURN(paragrah, 0);
290 return paragrah->GetLineCount();
291 #endif
292 }
293
GetCharacterWidth(int32_t index)294 float TxtParagraph::GetCharacterWidth(int32_t index)
295 {
296 auto paragrah = GetParagraph();
297 CHECK_NULL_RETURN(paragrah, 0.0f);
298 auto next = index + 1;
299 #ifndef USE_GRAPHIC_TEXT_GINE
300 auto boxes = paragrah->GetRectsForRange(
301 index, next, txt::Paragraph::RectHeightStyle::kMax, txt::Paragraph::RectWidthStyle::kTight);
302 #else
303 auto boxes = paragrah->GetTextRectsByBoundary(
304 index, next, Rosen::TextRectHeightStyle::COVER_TOP_AND_BOTTOM, Rosen::TextRectWidthStyle::TIGHT);
305 #endif
306 if (boxes.empty()) {
307 return 0.0f;
308 }
309 const auto& textBox = *boxes.begin();
310 #ifndef USE_GRAPHIC_TEXT_GINE
311 return textBox.rect.fRight - textBox.rect.fLeft;
312 #else
313 return textBox.rect.GetRight() - textBox.rect.GetLeft();
314 #endif
315 }
316
Paint(RSCanvas & canvas,float x,float y)317 void TxtParagraph::Paint(RSCanvas& canvas, float x, float y)
318 {
319 ACE_TEXT_SCOPED_TRACE("TxtParagraph::Paint");
320 auto paragrah = GetParagraph();
321 CHECK_NULL_VOID(paragrah);
322 #ifndef USE_ROSEN_DRAWING
323 SkCanvas* skCanvas = canvas.GetImpl<RSSkCanvas>()->ExportSkCanvas();
324 CHECK_NULL_VOID(skCanvas);
325 paragrah->Paint(skCanvas, x, y);
326 #else
327 paragrah->Paint(&canvas, x, y);
328 #endif
329 if (paraStyle_.leadingMargin && paraStyle_.leadingMargin->pixmap) {
330 CalculateLeadingMarginOffest(x, y);
331 auto canvasImage = PixelMapImage::Create(paraStyle_.leadingMargin->pixmap);
332 auto pixelMapImage = DynamicCast<PixelMapImage>(canvasImage);
333 CHECK_NULL_VOID(pixelMapImage);
334 auto& rsCanvas = const_cast<RSCanvas&>(canvas);
335 auto size = paraStyle_.leadingMargin->size;
336 auto width = static_cast<float>(size.Width().ConvertToPx());
337 auto height = static_cast<float>(size.Height().ConvertToPx());
338 pixelMapImage->DrawRect(rsCanvas, ToRSRect(RectF(x, y, width, height)));
339 }
340 }
341
CalculateLeadingMarginOffest(float & x,float & y)342 void TxtParagraph::CalculateLeadingMarginOffest(float& x, float& y)
343 {
344 auto paragrah = GetParagraph();
345 CHECK_NULL_VOID(paragrah);
346 auto lineCount = static_cast<int32_t>(GetLineCount());
347 CHECK_NULL_VOID(lineCount);
348 auto firstLineMetrics = GetLineMetrics(0);
349 auto size = paraStyle_.leadingMargin->size;
350 auto start = x;
351 if (paraStyle_.direction == TextDirection::RTL) {
352 x += static_cast<float>(firstLineMetrics.x + firstLineMetrics.width);
353 } else {
354 x += static_cast<float>(firstLineMetrics.x - size.Width().ConvertToPx());
355 }
356 x = std::max(start, x);
357 auto sizeRect =
358 SizeF(static_cast<float>(size.Width().ConvertToPx()), static_cast<float>(size.Height().ConvertToPx()));
359 y += Alignment::GetAlignPosition(
360 SizeF(sizeRect.Width(), static_cast<float>(firstLineMetrics.height)), sizeRect, paraStyle_.leadingMarginAlign)
361 .GetY();
362 }
363
364 #ifndef USE_ROSEN_DRAWING
Paint(SkCanvas * skCanvas,float x,float y)365 void TxtParagraph::Paint(SkCanvas* skCanvas, float x, float y)
366 {
367 auto paragrah = GetParagraph();
368 CHECK_NULL_VOID(skCanvas && paragrah);
369 paragrah->Paint(skCanvas, x, y);
370 }
371 #endif
372
373 // ToDo:adjust index
GetGlyphIndexByCoordinate(const Offset & offset,bool isSelectionPos)374 int32_t TxtParagraph::GetGlyphIndexByCoordinate(const Offset& offset, bool isSelectionPos)
375 {
376 auto paragrah = GetParagraph();
377 if (!paragrah) {
378 return 0;
379 }
380 int32_t index;
381 #ifndef USE_GRAPHIC_TEXT_GINE
382 index = static_cast<int32_t>(paragrah->GetGlyphPositionAtCoordinate(offset.GetX(), offset.GetY()).position);
383 #else
384 index = static_cast<int32_t>(paragrah->GetGlyphIndexByCoordinate(offset.GetX(), offset.GetY()).index);
385 #endif
386 if (isSelectionPos) {
387 AdjustIndexForward(offset, true, index);
388 }
389 return index;
390 }
391
GetGlyphPositionAtCoordinate(const Offset & offset)392 PositionWithAffinity TxtParagraph::GetGlyphPositionAtCoordinate(const Offset& offset)
393 {
394 PositionWithAffinity finalResult(0, TextAffinity::UPSTREAM);
395 auto paragrah = GetParagraph();
396 CHECK_NULL_RETURN(paragrah, finalResult);
397 #ifndef USE_GRAPHIC_TEXT_GINE
398 auto result = paragrah->GetGlyphPositionAtCoordinate(offset.GetX(), offset.GetY());
399 finalResult.position_ = result.pos_;
400 finalResult.affinity_ = static_cast<TextAffinity>(result.affinity_);
401 #else
402 auto result = paragrah->GetGlyphIndexByCoordinate(offset.GetX(), offset.GetY());
403 finalResult.position_ = result.index;
404 finalResult.affinity_ = static_cast<TextAffinity>(result.affinity);
405 #endif
406 return finalResult;
407 }
408
AdjustIndexForward(const Offset & offset,bool compareOffset,int32_t & index)409 void TxtParagraph::AdjustIndexForward(const Offset& offset, bool compareOffset, int32_t& index)
410 {
411 if (index < 0) {
412 index = 0;
413 return;
414 }
415 auto totalLen = static_cast<size_t>(placeholderCnt_) + text_.length();
416 if (static_cast<unsigned int>(index) == totalLen) {
417 --index;
418 AdjustIndexForward(offset, false, index);
419 return;
420 }
421 auto next = index + 1;
422 auto start = 0;
423 auto end = 0;
424 if (IsIndexInEmoji(index, start, end)) {
425 index = start;
426 next = end;
427 }
428 auto paragrah = GetParagraph();
429 #ifndef USE_GRAPHIC_TEXT_GINE
430 auto boxes = paragrah->GetRectsForRange(
431 index, next, txt::Paragraph::RectHeightStyle::kMax, txt::Paragraph::RectWidthStyle::kTight);
432 #else
433 auto boxes = paragrah->GetTextRectsByBoundary(
434 index, next, Rosen::TextRectHeightStyle::COVER_TOP_AND_BOTTOM, Rosen::TextRectWidthStyle::TIGHT);
435 #endif
436 if (boxes.empty()) {
437 --index;
438 AdjustIndexForward(offset, false, index);
439 return;
440 }
441 const auto& textBox = *boxes.begin();
442 #ifndef USE_GRAPHIC_TEXT_GINE
443 auto left = textBox.rect.fLeft;
444 auto right = textBox.rect.fRight;
445 auto top = textBox.rect.fTop;
446 #else
447 auto left = textBox.rect.GetLeft();
448 auto right = textBox.rect.GetRight();
449 auto top = textBox.rect.GetTop();
450 #endif
451 if (compareOffset && (LessNotEqual(offset.GetY(), top) || LessNotEqual(offset.GetX(), left))) {
452 --index;
453 AdjustIndexForward(offset, false, index);
454 } else if (NearEqual(left, right)) {
455 --index;
456 AdjustIndexForward(offset, false, index);
457 }
458 }
459
CalCulateAndCheckPreIsPlaceholder(int32_t index,int32_t & extent)460 bool TxtParagraph::CalCulateAndCheckPreIsPlaceholder(int32_t index, int32_t& extent)
461 {
462 for (auto placeholderIndex : placeholderPosition_) {
463 if (placeholderIndex == static_cast<size_t>(index)) {
464 return true;
465 } else if (placeholderIndex < static_cast<size_t>(index)) {
466 extent--;
467 }
468 }
469 return false;
470 }
471
ComputeOffsetForCaretUpstream(int32_t extent,CaretMetricsF & result,bool needLineHighest)472 bool TxtParagraph::ComputeOffsetForCaretUpstream(int32_t extent, CaretMetricsF& result, bool needLineHighest)
473 {
474 auto paragrah = GetParagraph();
475 if (!paragrah) {
476 return false;
477 }
478 if (empty()) {
479 return HandleCaretWhenEmpty(result);
480 }
481 if (static_cast<size_t>(extent) > GetParagraphLength()) {
482 extent = static_cast<int32_t>(GetParagraphLength());
483 }
484
485 extent = AdjustIndexForEmoji(extent);
486 char16_t prevChar = 0;
487 if (static_cast<size_t>(extent) <= text_.length()) {
488 prevChar = text_[std::max(0, extent - 1)];
489 }
490
491 result.Reset();
492 int32_t graphemeClusterLength = StringUtils::NotInUtf16Bmp(prevChar) ? 2 : 1;
493 int32_t prev = extent - graphemeClusterLength;
494 #ifndef USE_GRAPHIC_TEXT_GINE
495 auto boxes = paragrah->GetRectsForRange(
496 prev, extent, txt::Paragraph::RectHeightStyle::kMax, txt::Paragraph::RectWidthStyle::kTight);
497 #else
498 auto boxes = paragrah->GetTextRectsByBoundary(prev, extent,
499 needLineHighest ? Rosen::TextRectHeightStyle::COVER_TOP_AND_BOTTOM : Rosen::TextRectHeightStyle::TIGHT,
500 Rosen::TextRectWidthStyle::TIGHT);
501 #endif
502 while (boxes.empty() && !text_.empty()) {
503 graphemeClusterLength *= 2;
504 prev = extent - graphemeClusterLength;
505 if (prev < 0) {
506 #ifndef USE_GRAPHIC_TEXT_GINE
507 boxes = paragrah->GetRectsForRange(
508 0, extent, txt::Paragraph::RectHeightStyle::kMax, txt::Paragraph::RectWidthStyle::kTight);
509 #else
510 boxes = paragrah->GetTextRectsByBoundary(0, extent,
511 needLineHighest ? Rosen::TextRectHeightStyle::COVER_TOP_AND_BOTTOM : Rosen::TextRectHeightStyle::TIGHT,
512 Rosen::TextRectWidthStyle::TIGHT);
513 #endif
514 break;
515 }
516 #ifndef USE_GRAPHIC_TEXT_GINE
517 boxes = paragrah->GetRectsForRange(
518 prev, extent, txt::Paragraph::RectHeightStyle::kMax, txt::Paragraph::RectWidthStyle::kTight);
519 #else
520 boxes = paragrah->GetTextRectsByBoundary(prev, static_cast<size_t>(extent),
521 needLineHighest ? Rosen::TextRectHeightStyle::COVER_TOP_AND_BOTTOM : Rosen::TextRectHeightStyle::TIGHT,
522 Rosen::TextRectWidthStyle::TIGHT);
523 #endif
524 }
525 if (boxes.empty()) {
526 return false;
527 }
528
529 const auto& textBox = boxes.back();
530
531 // when text_ ends with a \n, return the top position of the next line.
532 auto preIsPlaceholder = CalCulateAndCheckPreIsPlaceholder(extent - 1, extent);
533 prevChar = text_[std::max(0, extent - 1)];
534 if (prevChar == NEWLINE_CODE && !text_[static_cast<size_t>(extent)] && !preIsPlaceholder) {
535 // Return the start of next line.
536 float y = 0.0f;
537 #ifndef USE_GRAPHIC_TEXT_GINE
538 y = textBox.rect.fBottom;
539 result.height = textBox.rect.fBottom - textBox.rect.fTop;
540 #else
541 y = textBox.rect.GetBottom();
542 result.height = textBox.rect.GetBottom() - textBox.rect.GetTop();
543 #endif
544 if (LessNotEqual(y, paragrah->GetHeight())) {
545 result.offset.SetX(MakeEmptyOffsetX());
546 result.offset.SetY(y);
547 return true;
548 }
549 }
550
551 #ifndef USE_GRAPHIC_TEXT_GINE
552 bool isLtr = textBox.direction == txt::TextDirection::ltr;
553 #else
554 if (isnan(textBox.rect.GetRight()) || isnan(textBox.rect.GetLeft())) {
555 LOGI("Right or left of textBox is NaN.");
556 return false;
557 }
558 bool isLtr = textBox.direction == Rosen::TextDirection::LTR;
559 #endif
560 // Caret is within width of the downstream glyphs.
561 #ifndef USE_GRAPHIC_TEXT_GINE
562 double caretStart = isLtr ? textBox.rect.fRight : textBox.rect.fLeft;
563 #else
564 double caretStart = isLtr ? textBox.rect.GetRight() : textBox.rect.GetLeft();
565 #endif
566 float offsetX = std::min(
567 static_cast<float>(caretStart), std::max(GetLongestLine(), static_cast<float>(paragrah->GetMaxWidth())));
568 result.offset.SetX(offsetX);
569 #ifndef USE_GRAPHIC_TEXT_GINE
570 result.offset.SetY(textBox.rect.fTop);
571 result.height = textBox.rect.fBottom - textBox.rect.fTop;
572 #else
573 result.offset.SetY(textBox.rect.GetTop());
574 result.height = textBox.rect.GetBottom() - textBox.rect.GetTop();
575 #endif
576
577 return true;
578 }
579
MakeEmptyOffsetX()580 float TxtParagraph::MakeEmptyOffsetX()
581 {
582 auto width = GetMaxWidth();
583 switch (textAlign_) {
584 case TextAlign::CENTER:
585 return width * 0.5f;
586 case TextAlign::END:
587 return width;
588 case TextAlign::START:
589 default:
590 return 0.0f;
591 }
592 }
593
ComputeOffsetForCaretDownstream(int32_t extent,CaretMetricsF & result,bool needLineHighest)594 bool TxtParagraph::ComputeOffsetForCaretDownstream(int32_t extent, CaretMetricsF& result, bool needLineHighest)
595 {
596 auto paragrah = GetParagraph();
597 if (!paragrah || static_cast<size_t>(extent) >= GetParagraphLength()) {
598 return false;
599 }
600
601 result.Reset();
602 #ifndef USE_GRAPHIC_TEXT_GINE
603 auto getTextRects = [parapraph = paragrah](int32_t extent, int32_t next) {
604 return parapraph->GetRectsForRange(
605 extent, next, txt::Paragraph::RectHeightStyle::kMax, txt::Paragraph::RectWidthStyle::kTight);
606 };
607 #else
608 auto getTextRects = [parapraph = paragrah, needLineHighest](int32_t extent, int32_t next) {
609 return parapraph->GetTextRectsByBoundary(extent, next,
610 needLineHighest ? Rosen::TextRectHeightStyle::COVER_TOP_AND_BOTTOM : Rosen::TextRectHeightStyle::TIGHT,
611 Rosen::TextRectWidthStyle::TIGHT);
612 };
613 #endif
614 extent = AdjustIndexForEmoji(extent);
615 auto boxes = getTextRects(extent, extent + 1);
616 if (boxes.empty() && !text_.empty()) {
617 boxes = getTextRects(extent, extent + LENGTH_INCREMENT);
618
619 int32_t start = 0;
620 int32_t end = 0;
621 // it could be emoji.
622 if (boxes.empty() && GetWordBoundary(extent, start, end)) {
623 boxes = getTextRects(extent, end);
624 }
625 }
626
627 if (boxes.empty()) {
628 return false;
629 }
630
631 const auto& textBox = *boxes.begin();
632 #ifndef USE_GRAPHIC_TEXT_GINE
633 bool isLtr = textBox.direction == txt::TextDirection::ltr;
634 double caretStart = isLtr ? textBox.rect.fLeft : textBox.rect.fRight;
635 #else
636 bool isLtr = textBox.direction == Rosen::TextDirection::LTR;
637 double caretStart = isLtr ? textBox.rect.GetLeft() : textBox.rect.GetRight();
638 #endif
639 // Caret is within width of the downstream glyphs.
640 double offsetX = std::min(caretStart, paragrah->GetMaxWidth());
641 result.offset.SetX(offsetX);
642 #ifndef USE_GRAPHIC_TEXT_GINE
643 result.offset.SetY(textBox.rect.fTop);
644 result.height = textBox.rect.fBottom - textBox.rect.fTop;
645 #else
646 result.offset.SetY(textBox.rect.GetTop());
647 result.height = textBox.rect.GetBottom() - textBox.rect.GetTop();
648 #endif
649
650 return true;
651 }
652
GetRectsForRange(int32_t start,int32_t end,std::vector<RectF> & selectedRects)653 void TxtParagraph::GetRectsForRange(int32_t start, int32_t end, std::vector<RectF>& selectedRects)
654 {
655 auto adjustStart = AdjustIndexForEmoji(start);
656 auto adjustEnd = AdjustIndexForEmoji(end);
657 GetRectsForRangeInner(adjustStart, adjustEnd, selectedRects, RectHeightPolicy::COVER_LINE);
658 }
659
GetTightRectsForRange(int32_t start,int32_t end,std::vector<RectF> & selectedRects)660 void TxtParagraph::GetTightRectsForRange(int32_t start, int32_t end, std::vector<RectF>& selectedRects)
661 {
662 auto adjustStart = AdjustIndexForEmoji(start);
663 auto adjustEnd = AdjustIndexForEmoji(end);
664 GetRectsForRangeInner(adjustStart, adjustEnd, selectedRects, RectHeightPolicy::COVER_TEXT);
665 }
666
GetRectsForRangeInner(int32_t start,int32_t end,std::vector<RectF> & selectedRects,RectHeightPolicy rectHeightPolicy)667 void TxtParagraph::GetRectsForRangeInner(int32_t start, int32_t end, std::vector<RectF>& selectedRects,
668 RectHeightPolicy rectHeightPolicy)
669 {
670 auto paragrah = GetParagraph();
671 CHECK_NULL_VOID(paragrah);
672 #ifndef USE_GRAPHIC_TEXT_GINE
673 auto heightStyle = rectHeightPolicy == RectHeightPolicy::COVER_TEXT
674 ? txt::Paragraph::RectHeightStyle::kTight
675 : txt::Paragraph::RectHeightStyle::kMax;
676 const auto& boxes = paragrah->GetRectsForRange(start, end, heightStyle, txt::Paragraph::RectWidthStyle::kTight);
677 #else
678 auto heightStyle = rectHeightPolicy == RectHeightPolicy::COVER_TEXT
679 ? Rosen::TextRectHeightStyle::TIGHT
680 : Rosen::TextRectHeightStyle::COVER_TOP_AND_BOTTOM;
681 const auto& boxes = paragrah->GetTextRectsByBoundary(start, end, heightStyle, Rosen::TextRectWidthStyle::TIGHT);
682 #endif
683 if (boxes.empty()) {
684 return;
685 }
686 for (const auto& box : boxes) {
687 auto rect = Constants::ConvertSkRect(box.rect);
688 RectF selectionRect(static_cast<float>(rect.Left()), static_cast<float>(rect.Top()),
689 static_cast<float>(std::abs(rect.Width())), static_cast<float>(rect.Height()));
690 selectedRects.emplace_back(selectionRect);
691 }
692 }
693
AdjustIndexForEmoji(int32_t index)694 int32_t TxtParagraph::AdjustIndexForEmoji(int32_t index)
695 {
696 int32_t start = 0;
697 int32_t end = 0;
698 if (IsIndexInEmoji(index, start, end)) {
699 return end;
700 }
701 return index;
702 }
703
IsIndexInEmoji(int32_t index,int32_t & emojiStart,int32_t & emojiEnd)704 bool TxtParagraph::IsIndexInEmoji(int32_t index, int32_t& emojiStart, int32_t& emojiEnd)
705 {
706 auto paragrah = GetParagraph();
707 CHECK_NULL_RETURN(paragrah, false);
708 int32_t start = 0;
709 int32_t end = 0;
710 if (!GetWordBoundary(index, start, end)) {
711 return false;
712 }
713 std::vector<RectF> selectedRects;
714 // if index in emoji or the first or the last, selectedRects is empty and
715 // 'end' will be emoji's end index or 0 or the max index.
716 GetRectsForRangeInner(index, end, selectedRects);
717 if (selectedRects.empty()) {
718 emojiStart = start;
719 emojiEnd = end;
720 return true;
721 }
722 return false;
723 }
724
GetRectsForPlaceholders(std::vector<RectF> & selectedRects)725 void TxtParagraph::GetRectsForPlaceholders(std::vector<RectF>& selectedRects)
726 {
727 auto paragrah = GetParagraph();
728 CHECK_NULL_VOID(paragrah);
729 #ifndef USE_GRAPHIC_TEXT_GINE
730 const auto& boxes = paragrah->GetRectsForPlaceholders();
731 #else
732 const auto& boxes = paragrah->GetTextRectsOfPlaceholders();
733 #endif
734 if (boxes.empty()) {
735 return;
736 }
737 for (const auto& box : boxes) {
738 auto rect = Constants::ConvertSkRect(box.rect);
739 RectF selectionRect(static_cast<float>(rect.Left()), static_cast<float>(rect.Top()),
740 static_cast<float>(rect.Width()), static_cast<float>(rect.Height()));
741 selectedRects.emplace_back(selectionRect);
742 }
743 }
744
CalcCaretMetricsByPosition(int32_t extent,CaretMetricsF & caretCaretMetric,TextAffinity textAffinity,bool needLineHighest)745 bool TxtParagraph::CalcCaretMetricsByPosition(
746 int32_t extent, CaretMetricsF& caretCaretMetric, TextAffinity textAffinity, bool needLineHighest)
747 {
748 CaretMetricsF metrics;
749 bool computeSuccess = false;
750 if (textAffinity == TextAffinity::DOWNSTREAM) {
751 computeSuccess = ComputeOffsetForCaretDownstream(extent, metrics, needLineHighest) ||
752 ComputeOffsetForCaretUpstream(extent, metrics, needLineHighest);
753 } else {
754 computeSuccess = ComputeOffsetForCaretUpstream(extent, metrics, needLineHighest) ||
755 ComputeOffsetForCaretDownstream(extent, metrics, needLineHighest);
756 }
757 if (computeSuccess) {
758 if (metrics.height <= 0 || std::isnan(metrics.height)) {
759 // The reason may be text lines is exceed the paragraph maxline.
760 return false;
761 }
762 caretCaretMetric = metrics;
763 return true;
764 }
765 return false;
766 }
767
CalcCaretMetricsByPosition(int32_t extent,CaretMetricsF & caretCaretMetric,const OffsetF & lastTouchOffset,TextAffinity & textAffinity)768 bool TxtParagraph::CalcCaretMetricsByPosition(
769 int32_t extent, CaretMetricsF& caretCaretMetric, const OffsetF& lastTouchOffset, TextAffinity& textAffinity)
770 {
771 CaretMetricsF metricsUpstream;
772 CaretMetricsF metricsDownstream;
773 auto downStreamSuccess = ComputeOffsetForCaretDownstream(extent, metricsDownstream);
774 auto upStreamSuccess = ComputeOffsetForCaretUpstream(extent, metricsUpstream);
775 if (downStreamSuccess || upStreamSuccess) {
776 if ((metricsDownstream.offset.GetY() < lastTouchOffset.GetY()) && downStreamSuccess) {
777 caretCaretMetric = metricsDownstream;
778 textAffinity = TextAffinity::DOWNSTREAM;
779 } else if (upStreamSuccess) {
780 caretCaretMetric = metricsUpstream;
781 textAffinity = TextAffinity::UPSTREAM;
782 } else {
783 caretCaretMetric = metricsDownstream;
784 textAffinity = TextAffinity::DOWNSTREAM;
785 }
786 return true;
787 }
788 textAffinity = TextAffinity::DOWNSTREAM;
789 return false;
790 }
791
SetIndents(const std::vector<float> & indents)792 void TxtParagraph::SetIndents(const std::vector<float>& indents)
793 {
794 #ifndef USE_GRAPHIC_TEXT_GINE
795 auto* paragraphTxt = static_cast<txt::ParagraphTxt*>(GetParagraph());
796 #else
797 auto* paragraphTxt = static_cast<OHOS::Rosen::Typography*>(GetParagraph());
798 #endif
799 CHECK_NULL_VOID(paragraphTxt);
800 paragraphTxt->SetIndents(indents);
801 }
802
GetWordBoundary(int32_t offset,int32_t & start,int32_t & end)803 bool TxtParagraph::GetWordBoundary(int32_t offset, int32_t& start, int32_t& end)
804 {
805 auto paragrah = GetParagraph();
806 CHECK_NULL_RETURN(paragrah, false);
807 #ifndef USE_GRAPHIC_TEXT_GINE
808 auto* paragraphTxt = static_cast<txt::ParagraphTxt*>(paragrah);
809 #else
810 auto* paragraphTxt = static_cast<OHOS::Rosen::Typography*>(paragrah);
811 #endif
812 CHECK_NULL_RETURN(paragraphTxt, false);
813 #ifndef USE_GRAPHIC_TEXT_GINE
814 auto range = paragraphTxt->GetWordBoundary(static_cast<size_t>(offset));
815 start = static_cast<int32_t>(range.start);
816 end = static_cast<int32_t>(range.end);
817 #else
818 auto range = paragraphTxt->GetWordBoundaryByIndex(static_cast<size_t>(offset));
819 start = static_cast<int32_t>(range.leftIndex);
820 end = static_cast<int32_t>(range.rightIndex);
821 #endif
822 return true;
823 }
824
HandleTextAlign(CaretMetricsF & result,TextAlign align)825 void TxtParagraph::HandleTextAlign(CaretMetricsF& result, TextAlign align)
826 {
827 auto width = GetMaxWidth();
828 float offsetX = 0.0f;
829 switch (align) {
830 case TextAlign::CENTER:
831 offsetX = width * 0.5f;
832 break;
833 case TextAlign::END:
834 offsetX = width;
835 break;
836 case TextAlign::START:
837 default:
838 break;
839 }
840 result.offset.SetX(offsetX);
841 }
842
HandleLeadingMargin(CaretMetricsF & result,LeadingMargin leadingMargin)843 void TxtParagraph::HandleLeadingMargin(CaretMetricsF& result, LeadingMargin leadingMargin)
844 {
845 result.offset.SetX(leadingMargin.size.Width().ConvertToPx());
846 }
847
HandleCaretWhenEmpty(CaretMetricsF & result)848 bool TxtParagraph::HandleCaretWhenEmpty(CaretMetricsF& result)
849 {
850 auto paragrah = GetParagraph();
851 if (!paragrah || paragrah->GetLineCount() == 0) {
852 return false;
853 }
854
855 result.offset.Reset();
856 #ifndef USE_GRAPHIC_TEXT_GINE
857 auto boxes = paragrah->GetRectsForRange(0, 1, txt::Paragraph::RectHeightStyle::kMax,
858 txt::Paragraph::RectWidthStyle::kTight);
859 #else
860 auto boxes = paragrah->GetTextRectsByBoundary(0, 1, Rosen::TextRectHeightStyle::TIGHT,
861 Rosen::TextRectWidthStyle::TIGHT);
862 #endif
863 if (boxes.empty()) {
864 result.height = paragrah->GetHeight();
865 auto lineHeight = paraStyle_.lineHeight;
866 if (lineHeight.IsValid()) {
867 result.offset.SetY(std::max(lineHeight.ConvertToPx() - result.height, 0.0));
868 }
869 } else {
870 const auto& textBox = boxes.back();
871 #ifndef USE_GRAPHIC_TEXT_GINE
872 result.height = textBox.rect.fBottom - textBox.rect.fTop;
873 result.offset.SetY(textBox.rect.fTop);
874 #else
875 result.height = textBox.rect.GetBottom() - textBox.rect.GetTop();
876 result.offset.SetY(textBox.rect.GetTop());
877 #endif
878 }
879
880 auto textAlign = paraStyle_.align;
881 if (paraStyle_.direction == TextDirection::RTL) {
882 if (textAlign == TextAlign::START) {
883 textAlign = TextAlign::END;
884 } else if (textAlign == TextAlign::END) {
885 textAlign = TextAlign::START;
886 }
887 }
888 if (textAlign != TextAlign::START) {
889 HandleTextAlign(result, textAlign);
890 } else {
891 if (paraStyle_.leadingMargin) {
892 HandleLeadingMargin(result, *(paraStyle_.leadingMargin));
893 }
894 result.offset.SetX(result.offset.GetX() + paraStyle_.indent.ConvertToPx());
895 }
896 return true;
897 }
898
GetLineMetricsByRectF(RectF & rect)899 LineMetrics TxtParagraph::GetLineMetricsByRectF(RectF& rect)
900 {
901 #ifndef USE_GRAPHIC_TEXT_GINE
902 auto* paragraphTxt = static_cast<txt::ParagraphTxt*>(GetParagraph());
903 #else
904 auto* paragraphTxt = static_cast<OHOS::Rosen::Typography*>(GetParagraph());
905 #endif
906 LineMetrics lineMetrics;
907 CHECK_NULL_RETURN(paragraphTxt, lineMetrics);
908 auto metrics = paragraphTxt->GetLineMetrics();
909 if (metrics.empty()) {
910 return lineMetrics;
911 }
912 auto top = std::floor(rect.Top() + 0.5);
913 auto bottom = std::floor(rect.Bottom() + 0.5);
914 auto res = metrics.size() - 1;
915 for (size_t index = 0; index < metrics.size() - 1; index++) {
916 if (metrics[index].y <= top && metrics[index + 1].y >= bottom) {
917 res = index;
918 break;
919 }
920 }
921 auto resMetric = metrics[res];
922 lineMetrics.x = resMetric.x;
923 lineMetrics.y = resMetric.y;
924 lineMetrics.ascender = resMetric.ascender;
925 lineMetrics.height = resMetric.height;
926 return lineMetrics;
927 }
928
GetLineMetrics(size_t lineNumber)929 TextLineMetrics TxtParagraph::GetLineMetrics(size_t lineNumber)
930 {
931 #ifndef USE_GRAPHIC_TEXT_GINE
932 auto* paragraphTxt = static_cast<txt::ParagraphTxt*>(GetParagraph());
933 #else
934 auto* paragraphTxt = static_cast<OHOS::Rosen::Typography*>(GetParagraph());
935 #endif
936 TextLineMetrics lineMetrics;
937 OHOS::Rosen::LineMetrics resMetric;
938 CHECK_NULL_RETURN(paragraphTxt, lineMetrics);
939 paragraphTxt->GetLineMetricsAt(lineNumber, &resMetric);
940
941 lineMetrics.ascender = resMetric.ascender;
942 lineMetrics.descender = resMetric.descender;
943 lineMetrics.capHeight = resMetric.capHeight;
944 lineMetrics.xHeight = resMetric.xHeight;
945 lineMetrics.width = resMetric.width;
946 lineMetrics.height = resMetric.height;
947 lineMetrics.x = resMetric.x;
948 lineMetrics.y = resMetric.y;
949 lineMetrics.startIndex = resMetric.startIndex;
950 lineMetrics.endIndex = resMetric.endIndex;
951 lineMetrics.baseline = resMetric.baseline;
952 lineMetrics.lineNumber = resMetric.lineNumber;
953
954 if (resMetric.runMetrics.empty()) {
955 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "GetLineMetrics runMetrics is empty.");
956 return lineMetrics;
957 }
958
959 auto runMetricsResMap = resMetric.runMetrics;
960 for (const auto& it : runMetricsResMap) {
961 RunMetrics runMetrics;
962 auto runMetricsRes = it.second;
963 SetRunMetrics(runMetrics, runMetricsRes);
964 lineMetrics.runMetrics.insert(std::map<size_t, RunMetrics>::value_type(it.first, runMetrics));
965 }
966 return lineMetrics;
967 }
968
SetRunMetrics(RunMetrics & runMetrics,const OHOS::Rosen::RunMetrics & runMetricsRes)969 void TxtParagraph::SetRunMetrics(RunMetrics& runMetrics, const OHOS::Rosen::RunMetrics& runMetricsRes)
970 {
971 auto textStyleRes = runMetricsRes.textStyle;
972 runMetrics.textStyle.SetTextDecoration(static_cast<TextDecoration>(textStyleRes->decoration));
973 Color color;
974 color.SetValue(textStyleRes->color.CastToColorQuad());
975 runMetrics.textStyle.SetTextColor(color);
976 runMetrics.textStyle.SetFontWeight(static_cast<FontWeight>(textStyleRes->fontWeight));
977 runMetrics.textStyle.SetFontStyle(static_cast<FontStyle>(textStyleRes->fontStyle));
978 runMetrics.textStyle.SetTextBaseline(static_cast<TextBaseline>(textStyleRes->baseline));
979 runMetrics.textStyle.SetFontFamilies(textStyleRes->fontFamilies);
980 runMetrics.textStyle.SetFontSize(Dimension(textStyleRes->fontSize, DimensionUnit::VP));
981 runMetrics.textStyle.SetLetterSpacing(Dimension(textStyleRes->letterSpacing, DimensionUnit::VP));
982 runMetrics.textStyle.SetWordSpacing(Dimension(textStyleRes->wordSpacing, DimensionUnit::VP));
983 runMetrics.textStyle.SetHeightScale(textStyleRes->heightScale);
984 runMetrics.textStyle.SetHalfLeading(textStyleRes->halfLeading);
985 runMetrics.textStyle.SetHeightOnly(textStyleRes->heightOnly);
986 auto& ellipsis = textStyleRes->ellipsis;
987 if (ellipsis == StringUtils::DEFAULT_USTRING || ellipsis.empty()) {
988 runMetrics.textStyle.SetEllipsis(u"");
989 } else {
990 runMetrics.textStyle.SetEllipsis(ellipsis);
991 }
992 runMetrics.textStyle.SetEllipsisMode(static_cast<EllipsisMode>(textStyleRes->ellipsisModal));
993 runMetrics.textStyle.SetLocale(textStyleRes->locale);
994
995 auto fontMetricsRes = runMetricsRes.fontMetrics;
996 runMetrics.fontMetrics.fFlags = fontMetricsRes.fFlags;
997 runMetrics.fontMetrics.fTop = fontMetricsRes.fTop;
998 runMetrics.fontMetrics.fAscent = fontMetricsRes.fAscent;
999 runMetrics.fontMetrics.fDescent = fontMetricsRes.fDescent;
1000 runMetrics.fontMetrics.fBottom = fontMetricsRes.fBottom;
1001 runMetrics.fontMetrics.fLeading = fontMetricsRes.fLeading;
1002 runMetrics.fontMetrics.fAvgCharWidth = fontMetricsRes.fAvgCharWidth;
1003 runMetrics.fontMetrics.fMaxCharWidth = fontMetricsRes.fMaxCharWidth;
1004 runMetrics.fontMetrics.fXMin = fontMetricsRes.fXMin;
1005 runMetrics.fontMetrics.fXMax = fontMetricsRes.fXMax;
1006 runMetrics.fontMetrics.fXHeight = fontMetricsRes.fXHeight;
1007 runMetrics.fontMetrics.fCapHeight = fontMetricsRes.fCapHeight;
1008 runMetrics.fontMetrics.fUnderlineThickness = fontMetricsRes.fUnderlineThickness;
1009 runMetrics.fontMetrics.fUnderlinePosition = fontMetricsRes.fUnderlinePosition;
1010 runMetrics.fontMetrics.fStrikeoutThickness = fontMetricsRes.fStrikeoutThickness;
1011 runMetrics.fontMetrics.fStrikeoutPosition = fontMetricsRes.fStrikeoutPosition;
1012 }
1013
GetLineMetricsByCoordinate(const Offset & offset,LineMetrics & lineMetrics)1014 bool TxtParagraph::GetLineMetricsByCoordinate(const Offset& offset, LineMetrics& lineMetrics)
1015 {
1016 #ifndef USE_GRAPHIC_TEXT_GINE
1017 auto* paragraphTxt = static_cast<txt::ParagraphTxt*>(GetParagraph());
1018 #else
1019 auto* paragraphTxt = static_cast<OHOS::Rosen::Typography*>(GetParagraph());
1020 #endif
1021 CHECK_NULL_RETURN(paragraphTxt, false);
1022 auto lineCount = static_cast<int32_t>(GetLineCount());
1023 if (lineCount <= 0) {
1024 return false;
1025 }
1026 auto height = GetHeight();
1027 if (height <= 0) {
1028 return false;
1029 }
1030 auto averageLineHeight = height / lineCount;
1031 auto lineNumber = std::clamp(static_cast<int32_t>(offset.GetY() / averageLineHeight), 0, lineCount - 1);
1032 Rosen::LineMetrics resMetric;
1033 auto ret = paragraphTxt->GetLineMetricsAt(lineNumber, &resMetric);
1034 while (ret) {
1035 if (GreatOrEqual(offset.GetY(), resMetric.y) && LessOrEqual(offset.GetY(), resMetric.y + resMetric.height)) {
1036 break;
1037 }
1038 if (LessNotEqual(offset.GetY(), resMetric.y)) {
1039 lineNumber--;
1040 ret = paragraphTxt->GetLineMetricsAt(lineNumber, &resMetric);
1041 continue;
1042 }
1043 if (GreatNotEqual(offset.GetY(), resMetric.y + resMetric.height)) {
1044 lineNumber++;
1045 ret = paragraphTxt->GetLineMetricsAt(lineNumber, &resMetric);
1046 continue;
1047 }
1048 ret = false;
1049 }
1050 if (ret) {
1051 lineMetrics.x = resMetric.x;
1052 lineMetrics.y = resMetric.y;
1053 lineMetrics.ascender = resMetric.ascender;
1054 lineMetrics.width = resMetric.width;
1055 lineMetrics.height = resMetric.height;
1056 lineMetrics.descender = resMetric.descender;
1057 lineMetrics.capHeight = resMetric.capHeight;
1058 lineMetrics.xHeight = resMetric.xHeight;
1059 }
1060 return ret;
1061 }
1062
GetParagraphText()1063 std::u16string TxtParagraph::GetParagraphText()
1064 {
1065 return text_;
1066 }
1067
GetParagraphStyle() const1068 const ParagraphStyle& TxtParagraph::GetParagraphStyle() const
1069 {
1070 return paraStyle_;
1071 }
1072
1073 #ifndef USE_GRAPHIC_TEXT_GINE
GetParagraph()1074 txt::Paragraph* TxtParagraph::GetParagraph()
1075 {
1076 return paragraph_.get();
1077 }
1078 #else
GetParagraph()1079 RSParagraph* TxtParagraph::GetParagraph()
1080 {
1081 if (paragraph_) {
1082 return paragraph_.get();
1083 }
1084 return externalParagraph_;
1085 }
1086 #endif
1087
UpdateColor(size_t from,size_t to,const Color & color)1088 void TxtParagraph::UpdateColor(size_t from, size_t to, const Color& color)
1089 {
1090 #ifndef USE_GRAPHIC_TEXT_GINE
1091 #else
1092 auto paragrah = GetParagraph();
1093 CHECK_NULL_VOID(paragrah);
1094 auto* paragraphTxt = static_cast<OHOS::Rosen::Typography*>(paragrah);
1095 CHECK_NULL_VOID(paragraphTxt);
1096 paragraphTxt->UpdateColor(from, to, ToRSColor(color));
1097 #endif
1098 }
1099 } // namespace OHOS::Ace::NG
1100