• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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