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