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 
TxtGetRectsForRange(int32_t start,int32_t end,RectHeightStyle heightStyle,RectWidthStyle widthStyle,std::vector<RectF> & selectedRects,std::vector<TextDirection> & textDirections)694 void TxtParagraph::TxtGetRectsForRange(int32_t start, int32_t end,
695     RectHeightStyle heightStyle, RectWidthStyle widthStyle,
696     std::vector<RectF>& selectedRects, std::vector<TextDirection>& textDirections)
697 {
698     auto paragrah = GetParagraph();
699     CHECK_NULL_VOID(paragrah);
700 #ifndef USE_GRAPHIC_TEXT_GINE
701     const auto& boxes = paragrah->GetRectsForRange(
702         start, end, Constants::ConvertTxtRectHeightStyle(heightStyle), Constants::ConvertTxtRectWidthStyle(widthStyle));
703 #else
704     const auto& boxes = paragrah->GetTextRectsByBoundary(
705         start, end, Constants::ConvertTxtRectHeightStyle(heightStyle), Constants::ConvertTxtRectWidthStyle(widthStyle));
706 #endif
707     if (boxes.empty()) {
708         return;
709     }
710     for (const auto& box : boxes) {
711         auto rect = Constants::ConvertSkRect(box.rect);
712         auto textDirection = box.direction;
713         RectF selectionRect(static_cast<float>(rect.Left()), static_cast<float>(rect.Top()),
714             static_cast<float>(rect.Width()), static_cast<float>(rect.Height()));
715         selectedRects.emplace_back(selectionRect);
716         textDirections.emplace_back(static_cast<OHOS::Ace::TextDirection>(textDirection));
717     }
718 }
719 
AdjustIndexForEmoji(int32_t index)720 int32_t TxtParagraph::AdjustIndexForEmoji(int32_t index)
721 {
722     int32_t start = 0;
723     int32_t end = 0;
724     if (IsIndexInEmoji(index, start, end)) {
725         return end;
726     }
727     return index;
728 }
729 
IsIndexInEmoji(int32_t index,int32_t & emojiStart,int32_t & emojiEnd)730 bool TxtParagraph::IsIndexInEmoji(int32_t index, int32_t& emojiStart, int32_t& emojiEnd)
731 {
732     auto paragrah = GetParagraph();
733     CHECK_NULL_RETURN(paragrah, false);
734     int32_t start = 0;
735     int32_t end = 0;
736     if (!GetWordBoundary(index, start, end)) {
737         return false;
738     }
739     std::vector<RectF> selectedRects;
740     // if index in emoji or the first or the last, selectedRects is empty and
741     // 'end' will be emoji's end index or 0 or the max index.
742     GetRectsForRangeInner(index, end, selectedRects);
743     if (selectedRects.empty()) {
744         emojiStart = start;
745         emojiEnd = end;
746         return true;
747     }
748     return false;
749 }
750 
GetRectsForPlaceholders(std::vector<RectF> & selectedRects)751 void TxtParagraph::GetRectsForPlaceholders(std::vector<RectF>& selectedRects)
752 {
753     auto paragrah = GetParagraph();
754     CHECK_NULL_VOID(paragrah);
755 #ifndef USE_GRAPHIC_TEXT_GINE
756     const auto& boxes = paragrah->GetRectsForPlaceholders();
757 #else
758     const auto& boxes = paragrah->GetTextRectsOfPlaceholders();
759 #endif
760     if (boxes.empty()) {
761         return;
762     }
763     for (const auto& box : boxes) {
764         auto rect = Constants::ConvertSkRect(box.rect);
765         RectF selectionRect(static_cast<float>(rect.Left()), static_cast<float>(rect.Top()),
766             static_cast<float>(rect.Width()), static_cast<float>(rect.Height()));
767         selectedRects.emplace_back(selectionRect);
768     }
769 }
770 
CalcCaretMetricsByPosition(int32_t extent,CaretMetricsF & caretCaretMetric,TextAffinity textAffinity,bool needLineHighest)771 bool TxtParagraph::CalcCaretMetricsByPosition(
772     int32_t extent, CaretMetricsF& caretCaretMetric, TextAffinity textAffinity, bool needLineHighest)
773 {
774     CaretMetricsF metrics;
775     bool computeSuccess = false;
776     if (textAffinity == TextAffinity::DOWNSTREAM) {
777         computeSuccess = ComputeOffsetForCaretDownstream(extent, metrics, needLineHighest) ||
778                          ComputeOffsetForCaretUpstream(extent, metrics, needLineHighest);
779     } else {
780         computeSuccess = ComputeOffsetForCaretUpstream(extent, metrics, needLineHighest) ||
781                          ComputeOffsetForCaretDownstream(extent, metrics, needLineHighest);
782     }
783     if (computeSuccess) {
784         if (metrics.height <= 0 || std::isnan(metrics.height)) {
785             // The reason may be text lines is exceed the paragraph maxline.
786             return false;
787         }
788         caretCaretMetric = metrics;
789         return true;
790     }
791     return false;
792 }
793 
CalcCaretMetricsByPosition(int32_t extent,CaretMetricsF & caretCaretMetric,const OffsetF & lastTouchOffset,TextAffinity & textAffinity)794 bool TxtParagraph::CalcCaretMetricsByPosition(
795     int32_t extent, CaretMetricsF& caretCaretMetric, const OffsetF& lastTouchOffset, TextAffinity& textAffinity)
796 {
797     CaretMetricsF metricsUpstream;
798     CaretMetricsF metricsDownstream;
799     auto downStreamSuccess = ComputeOffsetForCaretDownstream(extent, metricsDownstream);
800     auto upStreamSuccess = ComputeOffsetForCaretUpstream(extent, metricsUpstream);
801     if (downStreamSuccess || upStreamSuccess) {
802         if ((metricsDownstream.offset.GetY() < lastTouchOffset.GetY()) && downStreamSuccess) {
803             caretCaretMetric = metricsDownstream;
804             textAffinity = TextAffinity::DOWNSTREAM;
805         } else if (upStreamSuccess) {
806             caretCaretMetric = metricsUpstream;
807             textAffinity = TextAffinity::UPSTREAM;
808         } else {
809             caretCaretMetric = metricsDownstream;
810             textAffinity = TextAffinity::DOWNSTREAM;
811         }
812         return true;
813     }
814     textAffinity = TextAffinity::DOWNSTREAM;
815     return false;
816 }
817 
SetIndents(const std::vector<float> & indents)818 void TxtParagraph::SetIndents(const std::vector<float>& indents)
819 {
820 #ifndef USE_GRAPHIC_TEXT_GINE
821     auto* paragraphTxt = static_cast<txt::ParagraphTxt*>(GetParagraph());
822 #else
823     auto* paragraphTxt = static_cast<OHOS::Rosen::Typography*>(GetParagraph());
824 #endif
825     CHECK_NULL_VOID(paragraphTxt);
826     paragraphTxt->SetIndents(indents);
827 }
828 
GetWordBoundary(int32_t offset,int32_t & start,int32_t & end)829 bool TxtParagraph::GetWordBoundary(int32_t offset, int32_t& start, int32_t& end)
830 {
831     auto paragrah = GetParagraph();
832     CHECK_NULL_RETURN(paragrah, false);
833 #ifndef USE_GRAPHIC_TEXT_GINE
834     auto* paragraphTxt = static_cast<txt::ParagraphTxt*>(paragrah);
835 #else
836     auto* paragraphTxt = static_cast<OHOS::Rosen::Typography*>(paragrah);
837 #endif
838     CHECK_NULL_RETURN(paragraphTxt, false);
839 #ifndef USE_GRAPHIC_TEXT_GINE
840     auto range = paragraphTxt->GetWordBoundary(static_cast<size_t>(offset));
841     start = static_cast<int32_t>(range.start);
842     end = static_cast<int32_t>(range.end);
843 #else
844     auto range = paragraphTxt->GetWordBoundaryByIndex(static_cast<size_t>(offset));
845     start = static_cast<int32_t>(range.leftIndex);
846     end = static_cast<int32_t>(range.rightIndex);
847 #endif
848     return true;
849 }
850 
HandleTextAlign(CaretMetricsF & result,TextAlign align)851 void TxtParagraph::HandleTextAlign(CaretMetricsF& result, TextAlign align)
852 {
853     auto width = GetMaxWidth();
854     float offsetX = 0.0f;
855     switch (align) {
856         case TextAlign::CENTER:
857             offsetX = width * 0.5f;
858             break;
859         case TextAlign::END:
860             offsetX = width;
861             break;
862         case TextAlign::START:
863         default:
864             break;
865     }
866     result.offset.SetX(offsetX);
867 }
868 
HandleLeadingMargin(CaretMetricsF & result,LeadingMargin leadingMargin)869 void TxtParagraph::HandleLeadingMargin(CaretMetricsF& result, LeadingMargin leadingMargin)
870 {
871     result.offset.SetX(leadingMargin.size.Width().ConvertToPx());
872 }
873 
HandleCaretWhenEmpty(CaretMetricsF & result)874 bool TxtParagraph::HandleCaretWhenEmpty(CaretMetricsF& result)
875 {
876     auto paragrah = GetParagraph();
877     if (!paragrah || paragrah->GetLineCount() == 0) {
878         return false;
879     }
880 
881     result.offset.Reset();
882 #ifndef USE_GRAPHIC_TEXT_GINE
883     auto boxes = paragrah->GetRectsForRange(0, 1, txt::Paragraph::RectHeightStyle::kMax,
884         txt::Paragraph::RectWidthStyle::kTight);
885 #else
886     auto boxes = paragrah->GetTextRectsByBoundary(0, 1, Rosen::TextRectHeightStyle::TIGHT,
887         Rosen::TextRectWidthStyle::TIGHT);
888 #endif
889     if (boxes.empty()) {
890         result.height = paragrah->GetHeight();
891         auto lineHeight = paraStyle_.lineHeight;
892         if (lineHeight.IsValid()) {
893             result.offset.SetY(std::max(lineHeight.ConvertToPx() - result.height, 0.0));
894         }
895     } else {
896         const auto& textBox = boxes.back();
897 #ifndef USE_GRAPHIC_TEXT_GINE
898         result.height = textBox.rect.fBottom - textBox.rect.fTop;
899         result.offset.SetY(textBox.rect.fTop);
900 #else
901         result.height = textBox.rect.GetBottom() - textBox.rect.GetTop();
902         result.offset.SetY(textBox.rect.GetTop());
903 #endif
904     }
905 
906     auto textAlign = paraStyle_.align;
907     if (paraStyle_.direction == TextDirection::RTL) {
908         if (textAlign == TextAlign::START) {
909             textAlign = TextAlign::END;
910         } else if (textAlign == TextAlign::END) {
911             textAlign = TextAlign::START;
912         }
913     }
914     if (textAlign != TextAlign::START) {
915         HandleTextAlign(result, textAlign);
916     } else {
917         if (paraStyle_.leadingMargin) {
918             HandleLeadingMargin(result, *(paraStyle_.leadingMargin));
919         }
920         result.offset.SetX(result.offset.GetX() + paraStyle_.indent.ConvertToPx());
921     }
922     return true;
923 }
924 
GetLineMetricsByRectF(RectF & rect)925 LineMetrics TxtParagraph::GetLineMetricsByRectF(RectF& rect)
926 {
927 #ifndef USE_GRAPHIC_TEXT_GINE
928     auto* paragraphTxt = static_cast<txt::ParagraphTxt*>(GetParagraph());
929 #else
930     auto* paragraphTxt = static_cast<OHOS::Rosen::Typography*>(GetParagraph());
931 #endif
932     LineMetrics lineMetrics;
933     CHECK_NULL_RETURN(paragraphTxt, lineMetrics);
934     auto metrics = paragraphTxt->GetLineMetrics();
935     if (metrics.empty()) {
936         return lineMetrics;
937     }
938     auto top = std::floor(rect.Top() + 0.5);
939     auto bottom = std::floor(rect.Bottom() + 0.5);
940     auto res = metrics.size() - 1;
941     for (size_t index = 0; index < metrics.size() - 1; index++) {
942         if (metrics[index].y <= top && metrics[index + 1].y >= bottom) {
943             res = index;
944             break;
945         }
946     }
947     auto resMetric = metrics[res];
948     lineMetrics.x = resMetric.x;
949     lineMetrics.y = resMetric.y;
950     lineMetrics.ascender = resMetric.ascender;
951     lineMetrics.height = resMetric.height;
952     return lineMetrics;
953 }
954 
GetLineMetrics(size_t lineNumber)955 TextLineMetrics TxtParagraph::GetLineMetrics(size_t lineNumber)
956 {
957 #ifndef USE_GRAPHIC_TEXT_GINE
958     auto* paragraphTxt = static_cast<txt::ParagraphTxt*>(GetParagraph());
959 #else
960     auto* paragraphTxt = static_cast<OHOS::Rosen::Typography*>(GetParagraph());
961 #endif
962     TextLineMetrics lineMetrics;
963     OHOS::Rosen::LineMetrics resMetric;
964     CHECK_NULL_RETURN(paragraphTxt, lineMetrics);
965     paragraphTxt->GetLineMetricsAt(lineNumber, &resMetric);
966 
967     lineMetrics.ascender = resMetric.ascender;
968     lineMetrics.descender = resMetric.descender;
969     lineMetrics.capHeight = resMetric.capHeight;
970     lineMetrics.xHeight = resMetric.xHeight;
971     lineMetrics.width = resMetric.width;
972     lineMetrics.height = resMetric.height;
973     lineMetrics.x = resMetric.x;
974     lineMetrics.y = resMetric.y;
975     lineMetrics.startIndex = resMetric.startIndex;
976     lineMetrics.endIndex = resMetric.endIndex;
977     lineMetrics.baseline = resMetric.baseline;
978     lineMetrics.lineNumber = resMetric.lineNumber;
979 
980     if (resMetric.runMetrics.empty()) {
981         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "GetLineMetrics runMetrics is empty.");
982         return lineMetrics;
983     }
984 
985     auto runMetricsResMap = resMetric.runMetrics;
986     for (const auto& it : runMetricsResMap) {
987         RunMetrics runMetrics;
988         auto runMetricsRes = it.second;
989         SetRunMetrics(runMetrics, runMetricsRes);
990         lineMetrics.runMetrics.insert(std::map<size_t, RunMetrics>::value_type(it.first, runMetrics));
991     }
992     return lineMetrics;
993 }
994 
GetPaintRegion(float x,float y)995 RectF TxtParagraph::GetPaintRegion(float x, float y)
996 {
997 #ifndef USE_GRAPHIC_TEXT_GINE
998     auto* paragraphTxt = static_cast<txt::ParagraphTxt*>(GetParagraph());
999 #else
1000     auto* paragraphTxt = static_cast<OHOS::Rosen::Typography*>(GetParagraph());
1001 #endif
1002     CHECK_NULL_RETURN(paragraphTxt, RectF());
1003     auto region = paragraphTxt->GeneratePaintRegion(x, y);
1004     return RectF(region.GetLeft(), region.GetTop(), region.GetWidth(), region.GetHeight());
1005 }
1006 
SetRunMetrics(RunMetrics & runMetrics,const OHOS::Rosen::RunMetrics & runMetricsRes)1007 void TxtParagraph::SetRunMetrics(RunMetrics& runMetrics, const OHOS::Rosen::RunMetrics& runMetricsRes)
1008 {
1009     auto textStyleRes = runMetricsRes.textStyle;
1010     runMetrics.textStyle.SetTextDecoration(static_cast<TextDecoration>(textStyleRes->decoration));
1011     Color color;
1012     color.SetValue(textStyleRes->color.CastToColorQuad());
1013     runMetrics.textStyle.SetTextColor(color);
1014     runMetrics.textStyle.SetFontWeight(static_cast<FontWeight>(textStyleRes->fontWeight));
1015     runMetrics.textStyle.SetFontStyle(static_cast<FontStyle>(textStyleRes->fontStyle));
1016     runMetrics.textStyle.SetTextBaseline(static_cast<TextBaseline>(textStyleRes->baseline));
1017     runMetrics.textStyle.SetFontFamilies(textStyleRes->fontFamilies);
1018     runMetrics.textStyle.SetFontSize(Dimension(textStyleRes->fontSize, DimensionUnit::VP));
1019     runMetrics.textStyle.SetLetterSpacing(Dimension(textStyleRes->letterSpacing, DimensionUnit::VP));
1020     runMetrics.textStyle.SetWordSpacing(Dimension(textStyleRes->wordSpacing, DimensionUnit::VP));
1021     runMetrics.textStyle.SetHeightScale(textStyleRes->heightScale);
1022     runMetrics.textStyle.SetHalfLeading(textStyleRes->halfLeading);
1023     runMetrics.textStyle.SetHeightOnly(textStyleRes->heightOnly);
1024     auto& ellipsis = textStyleRes->ellipsis;
1025     if (ellipsis == StringUtils::DEFAULT_USTRING || ellipsis.empty()) {
1026         runMetrics.textStyle.SetEllipsis(u"");
1027     } else {
1028         runMetrics.textStyle.SetEllipsis(ellipsis);
1029     }
1030     runMetrics.textStyle.SetEllipsisMode(static_cast<EllipsisMode>(textStyleRes->ellipsisModal));
1031     runMetrics.textStyle.SetLocale(textStyleRes->locale);
1032 
1033     auto fontMetricsRes = runMetricsRes.fontMetrics;
1034     runMetrics.fontMetrics.fFlags = fontMetricsRes.fFlags;
1035     runMetrics.fontMetrics.fTop = fontMetricsRes.fTop;
1036     runMetrics.fontMetrics.fAscent = fontMetricsRes.fAscent;
1037     runMetrics.fontMetrics.fDescent = fontMetricsRes.fDescent;
1038     runMetrics.fontMetrics.fBottom = fontMetricsRes.fBottom;
1039     runMetrics.fontMetrics.fLeading = fontMetricsRes.fLeading;
1040     runMetrics.fontMetrics.fAvgCharWidth = fontMetricsRes.fAvgCharWidth;
1041     runMetrics.fontMetrics.fMaxCharWidth = fontMetricsRes.fMaxCharWidth;
1042     runMetrics.fontMetrics.fXMin = fontMetricsRes.fXMin;
1043     runMetrics.fontMetrics.fXMax = fontMetricsRes.fXMax;
1044     runMetrics.fontMetrics.fXHeight = fontMetricsRes.fXHeight;
1045     runMetrics.fontMetrics.fCapHeight = fontMetricsRes.fCapHeight;
1046     runMetrics.fontMetrics.fUnderlineThickness = fontMetricsRes.fUnderlineThickness;
1047     runMetrics.fontMetrics.fUnderlinePosition = fontMetricsRes.fUnderlinePosition;
1048     runMetrics.fontMetrics.fStrikeoutThickness = fontMetricsRes.fStrikeoutThickness;
1049     runMetrics.fontMetrics.fStrikeoutPosition = fontMetricsRes.fStrikeoutPosition;
1050 }
1051 
GetLineMetricsByCoordinate(const Offset & offset,LineMetrics & lineMetrics)1052 bool TxtParagraph::GetLineMetricsByCoordinate(const Offset& offset, LineMetrics& lineMetrics)
1053 {
1054 #ifndef USE_GRAPHIC_TEXT_GINE
1055     auto* paragraphTxt = static_cast<txt::ParagraphTxt*>(GetParagraph());
1056 #else
1057     auto* paragraphTxt = static_cast<OHOS::Rosen::Typography*>(GetParagraph());
1058 #endif
1059     CHECK_NULL_RETURN(paragraphTxt, false);
1060     auto lineCount = static_cast<int32_t>(GetLineCount());
1061     if (lineCount <= 0) {
1062         return false;
1063     }
1064     auto height = GetHeight();
1065     if (height <= 0) {
1066         return false;
1067     }
1068     auto averageLineHeight = height / lineCount;
1069     auto lineNumber = std::clamp(static_cast<int32_t>(offset.GetY() / averageLineHeight), 0, lineCount - 1);
1070     Rosen::LineMetrics resMetric;
1071     auto ret = paragraphTxt->GetLineMetricsAt(lineNumber, &resMetric);
1072     while (ret) {
1073         if (GreatOrEqual(offset.GetY(), resMetric.y) && LessOrEqual(offset.GetY(), resMetric.y + resMetric.height)) {
1074             break;
1075         }
1076         if (LessNotEqual(offset.GetY(), resMetric.y)) {
1077             lineNumber--;
1078             ret = paragraphTxt->GetLineMetricsAt(lineNumber, &resMetric);
1079             continue;
1080         }
1081         if (GreatNotEqual(offset.GetY(), resMetric.y + resMetric.height)) {
1082             lineNumber++;
1083             ret = paragraphTxt->GetLineMetricsAt(lineNumber, &resMetric);
1084             continue;
1085         }
1086         ret = false;
1087     }
1088     if (ret) {
1089         lineMetrics.x = resMetric.x;
1090         lineMetrics.y = resMetric.y;
1091         lineMetrics.ascender = resMetric.ascender;
1092         lineMetrics.width = resMetric.width;
1093         lineMetrics.height = resMetric.height;
1094         lineMetrics.descender = resMetric.descender;
1095         lineMetrics.capHeight = resMetric.capHeight;
1096         lineMetrics.xHeight = resMetric.xHeight;
1097     }
1098     return ret;
1099 }
1100 
GetParagraphText()1101 std::u16string TxtParagraph::GetParagraphText()
1102 {
1103     return text_;
1104 }
1105 
GetParagraphStyle() const1106 const ParagraphStyle& TxtParagraph::GetParagraphStyle() const
1107 {
1108     return paraStyle_;
1109 }
1110 
1111 #ifndef USE_GRAPHIC_TEXT_GINE
GetParagraph()1112 txt::Paragraph* TxtParagraph::GetParagraph()
1113 {
1114     return paragraph_.get();
1115 }
1116 #else
GetParagraph()1117 RSParagraph* TxtParagraph::GetParagraph()
1118 {
1119     if (paragraph_) {
1120         return paragraph_.get();
1121     }
1122     return externalParagraph_;
1123 }
1124 #endif
1125 
UpdateColor(size_t from,size_t to,const Color & color)1126 void TxtParagraph::UpdateColor(size_t from, size_t to, const Color& color)
1127 {
1128 #ifndef USE_GRAPHIC_TEXT_GINE
1129 #else
1130     auto paragrah = GetParagraph();
1131     CHECK_NULL_VOID(paragrah);
1132     auto* paragraphTxt = static_cast<OHOS::Rosen::Typography*>(paragrah);
1133     CHECK_NULL_VOID(paragraphTxt);
1134     paragraphTxt->UpdateColor(from, to, ToRSColor(color));
1135 #endif
1136 }
1137 } // namespace OHOS::Ace::NG
1138