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