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