• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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/pattern/rich_editor/paragraph_manager.h"
17 
18 namespace OHOS::Ace::NG {
19 namespace {
20 constexpr float MIN_RECT_TOP = -0.4f;
21 constexpr float MIN_RECT_PRECISION = -0.5f;
22 } // namespace
23 
GetHeight() const24 float ParagraphManager::GetHeight() const
25 {
26     float res = 0.0f;
27     for (auto&& info : paragraphs_) {
28         auto paragraph = info.paragraph;
29         CHECK_NULL_RETURN(paragraph, 0.0f);
30         res += paragraph->GetHeight();
31     }
32     return res;
33 }
34 
GetMaxIntrinsicWidth() const35 float ParagraphManager::GetMaxIntrinsicWidth() const
36 {
37     float res = 0.0f;
38     for (auto &&info : paragraphs_) {
39         auto paragraph = info.paragraph;
40         CHECK_NULL_RETURN(paragraph, 0.0f);
41         res = std::max(res, paragraph->GetMaxIntrinsicWidth());
42     }
43     return res;
44 }
DidExceedMaxLines() const45 bool ParagraphManager::DidExceedMaxLines() const
46 {
47     bool res = false;
48     for (auto &&info : paragraphs_) {
49         auto paragraph = info.paragraph;
50         CHECK_NULL_RETURN(paragraph, false);
51         res |= paragraph->DidExceedMaxLines();
52     }
53     return res;
54 }
55 
DidExceedMaxLinesInner() const56 bool ParagraphManager::DidExceedMaxLinesInner() const
57 {
58     bool res = false;
59     for (auto&& info : paragraphs_) {
60         auto paragraph = info.paragraph;
61         CHECK_NULL_RETURN(paragraph, false);
62         res |= paragraph->DidExceedMaxLinesInner();
63     }
64     return res;
65 }
66 
GetDumpInfo() const67 std::string ParagraphManager::GetDumpInfo() const
68 {
69     std::string dumpInfo = "";
70     for (auto&& info : paragraphs_) {
71         dumpInfo += "[";
72         auto paragraph = info.paragraph;
73         CHECK_NULL_RETURN(paragraph, dumpInfo);
74         dumpInfo += paragraph->GetDumpInfo();
75         dumpInfo += "]";
76     }
77     return dumpInfo;
78 }
79 
GetLongestLine() const80 float ParagraphManager::GetLongestLine() const
81 {
82     float res = 0.0f;
83     for (auto &&info : paragraphs_) {
84         auto paragraph = info.paragraph;
85         CHECK_NULL_RETURN(paragraph, 0.0f);
86         res = std::max(res, paragraph->GetLongestLine());
87     }
88     return res;
89 }
GetMaxWidth() const90 float ParagraphManager::GetMaxWidth() const
91 {
92     float res = 0.0f;
93     for (auto &&info : paragraphs_) {
94         auto paragraph = info.paragraph;
95         CHECK_NULL_RETURN(paragraph, 0.0f);
96         res = std::max(res, paragraph->GetMaxWidth());
97     }
98     return res;
99 }
GetTextWidth() const100 float ParagraphManager::GetTextWidth() const
101 {
102     float res = 0.0f;
103     for (auto &&info : paragraphs_) {
104         auto paragraph = info.paragraph;
105         CHECK_NULL_RETURN(paragraph, 0.0f);
106         res = std::max(res, paragraph->GetTextWidth());
107     }
108     return res;
109 }
110 
GetTextWidthIncludeIndent() const111 float ParagraphManager::GetTextWidthIncludeIndent() const
112 {
113     float res = 0.0f;
114     for (auto &&info : paragraphs_) {
115         auto paragraph = info.paragraph;
116         CHECK_NULL_RETURN(paragraph, 0.0f);
117         auto width = paragraph->GetTextWidth();
118         res = std::max(res, width);
119     }
120     return res;
121 }
122 
GetLongestLineWithIndent() const123 float ParagraphManager::GetLongestLineWithIndent() const
124 {
125     float res = 0.0f;
126     for (auto &&info : paragraphs_) {
127         auto paragraph = info.paragraph;
128         CHECK_NULL_RETURN(paragraph, 0.0f);
129         auto width = paragraph->GetLongestLineWithIndent();
130         res = std::max(res, width);
131     }
132     return res;
133 }
134 
GetLineCount() const135 size_t ParagraphManager::GetLineCount() const
136 {
137     size_t count = 0;
138     for (auto &&info : paragraphs_) {
139         auto paragraph = info.paragraph;
140         CHECK_NULL_RETURN(paragraph, 0);
141         count += paragraph->GetLineCount();
142     }
143     return count;
144 }
145 
GetIndex(Offset offset,bool clamp) const146 int32_t ParagraphManager::GetIndex(Offset offset, bool clamp) const
147 {
148     CHECK_NULL_RETURN(!paragraphs_.empty(), 0);
149     if (clamp && LessNotEqual(offset.GetY(), 0.0)) {
150         return 0;
151     }
152     int idx = 0;
153     for (auto it = paragraphs_.begin(); it != paragraphs_.end(); ++it, ++idx) {
154         auto&& info = *it;
155         if (LessOrEqual(offset.GetY(), info.paragraph->GetHeight()) ||
156             (!clamp && idx == static_cast<int>(paragraphs_.size()) - 1)) {
157             return info.paragraph->GetGlyphIndexByCoordinate(offset) + info.start;
158         }
159         // get offset relative to each paragraph
160         offset.SetY(offset.GetY() - info.paragraph->GetHeight());
161     }
162     return paragraphs_.back().end;
163 }
164 
GetGlyphPositionAtCoordinate(Offset offset)165 PositionWithAffinity ParagraphManager::GetGlyphPositionAtCoordinate(Offset offset)
166 {
167     TAG_LOGI(AceLogTag::ACE_TEXT,
168         "Get Glyph Position, coordinate = [%{public}.2f %{public}.2f]", offset.GetX(), offset.GetY());
169     PositionWithAffinity finalResult(0, TextAffinity::UPSTREAM);
170     CHECK_NULL_RETURN(!paragraphs_.empty(), finalResult);
171     if (LessNotEqual(offset.GetY(), 0.0)) {
172         return finalResult;
173     }
174     int idx = 0;
175     for (auto it = paragraphs_.begin(); it != paragraphs_.end(); ++it, ++idx) {
176         auto& info = *it;
177         if (LessOrEqual(offset.GetY(), info.paragraph->GetHeight()) ||
178             (idx == static_cast<int>(paragraphs_.size()) - 1)) {
179             auto result = info.paragraph->GetGlyphPositionAtCoordinate(offset);
180             finalResult.position_ = result.position_ + static_cast<size_t>(info.start);
181             TAG_LOGI(AceLogTag::ACE_TEXT,
182                 "Current paragraph, originPos = %{public}zu, finalPos =%{public}zu and affinity = %{public}d",
183                 result.position_, finalResult.position_, result.affinity_);
184             finalResult.affinity_ = static_cast<TextAffinity>(result.affinity_);
185             return finalResult;
186         }
187         // get offset relative to each paragraph
188         offset.SetY(offset.GetY() - info.paragraph->GetHeight());
189     }
190     auto info = paragraphs_.back();
191     auto result = info.paragraph->GetGlyphPositionAtCoordinate(offset);
192     finalResult.position_ = static_cast<size_t>(info.end);
193     finalResult.affinity_ = static_cast<TextAffinity>(result.affinity_);
194     TAG_LOGI(AceLogTag::ACE_TEXT,
195         "Current paragraph, final position = %{public}zu and affinity = %{public}d", finalResult.position_,
196         finalResult.affinity_);
197     return finalResult;
198 }
199 
GetGlyphIndexByCoordinate(Offset offset,bool isSelectionPos) const200 int32_t ParagraphManager::GetGlyphIndexByCoordinate(Offset offset, bool isSelectionPos) const
201 {
202     CHECK_NULL_RETURN(!paragraphs_.empty(), 0);
203     for (auto it = paragraphs_.begin(); it != paragraphs_.end(); ++it) {
204         auto &&info = *it;
205         if (LessOrEqual(offset.GetY(), info.paragraph->GetHeight())) {
206             return info.paragraph->GetGlyphIndexByCoordinate(offset, isSelectionPos) + info.start;
207         }
208         // get offset relative to each paragraph
209         offset.SetY(offset.GetY() - info.paragraph->GetHeight());
210     }
211     offset.SetY(offset.GetY() + paragraphs_.back().paragraph->GetHeight());
212     return paragraphs_.back().paragraph->GetGlyphIndexByCoordinate(offset, isSelectionPos) + paragraphs_.back().start;
213 }
214 
GetWordBoundary(int32_t offset,int32_t & start,int32_t & end) const215 bool ParagraphManager::GetWordBoundary(int32_t offset, int32_t& start, int32_t& end) const
216 {
217     CHECK_NULL_RETURN(!paragraphs_.empty(), false);
218     auto offsetIndex = offset;
219     auto startIndex = 0;
220     auto endIndex = 0;
221     for (auto it = paragraphs_.begin(); it != paragraphs_.end(); ++it) {
222         auto &&info = *it;
223         if (LessNotEqual(offset, info.end)) {
224             auto flag = info.paragraph->GetWordBoundary(offsetIndex, start, end);
225             start += startIndex;
226             end += endIndex;
227             return flag;
228         }
229         // get offset relative to each paragraph
230         offsetIndex = offset - info.end;
231         startIndex = info.end;
232         endIndex = info.end;
233     }
234     return false;
235 }
236 
CalcCaretMetricsByPosition(int32_t extent,CaretMetricsF & caretCaretMetric,TextAffinity textAffinity) const237 bool ParagraphManager::CalcCaretMetricsByPosition(
238     int32_t extent, CaretMetricsF& caretCaretMetric, TextAffinity textAffinity) const
239 {
240     CHECK_NULL_RETURN(!paragraphs_.empty(), false);
241     auto offsetIndex = extent;
242     auto offsetY = 0.0f;
243     auto result = false;
244     for (auto it = paragraphs_.begin(); it != paragraphs_.end(); ++it) {
245         auto &&info = *it;
246         if (textAffinity == TextAffinity::UPSTREAM || std::next(it) == paragraphs_.end()) {
247             if (LessOrEqual(extent, info.end)) {
248                 result = info.paragraph->CalcCaretMetricsByPosition(offsetIndex, caretCaretMetric, textAffinity);
249                 break;
250             }
251         } else {
252             if (LessNotEqual(extent, info.end)) {
253                 result = info.paragraph->CalcCaretMetricsByPosition(offsetIndex, caretCaretMetric, textAffinity);
254                 break;
255             }
256         }
257         // get offset relative to each paragraph
258         offsetIndex = extent - info.end;
259         offsetY += info.paragraph->GetHeight();
260     }
261     caretCaretMetric.offset += OffsetF(0.0f, offsetY);
262     return result;
263 }
264 
GetLineMetricsByRectF(RectF rect,int32_t paragraphIndex) const265 LineMetrics ParagraphManager::GetLineMetricsByRectF(RectF rect, int32_t paragraphIndex) const
266 {
267     auto index = 0;
268     float height = 0;
269     auto iter = paragraphs_.begin();
270     while (index < paragraphIndex) {
271         auto paragraphInfo = *iter;
272         height += paragraphInfo.paragraph->GetHeight();
273         iter++;
274         index++;
275     }
276     auto paragraphInfo = *iter;
277     rect.SetTop(rect.GetY() - height);
278     auto lineMetrics = paragraphInfo.paragraph->GetLineMetricsByRectF(rect);
279     lineMetrics.y += height;
280     return lineMetrics;
281 }
282 
GetLineMetrics(size_t lineNumber)283 TextLineMetrics ParagraphManager::GetLineMetrics(size_t lineNumber)
284 {
285     if (GetLineCount() == 0 || lineNumber > GetLineCount() - 1) {
286         TAG_LOGE(AceLogTag::ACE_TEXT,
287             "GetLineMetrics failed, lineNumber is greater than max lines:%{public}zu", lineNumber);
288         return TextLineMetrics();
289     }
290     size_t endIndex = 0;
291     double paragraphsHeight = 0.0;
292     size_t lineNumberParam = lineNumber;
293     for (auto &&info : paragraphs_) {
294         auto lineCount = info.paragraph->GetLineCount();
295         if (lineCount > 0 && lineNumber > lineCount - 1) {
296             lineNumber -= lineCount;
297             paragraphsHeight += info.paragraph->GetHeight();
298             auto lastLineMetrics = info.paragraph->GetLineMetrics(lineCount - 1);
299             endIndex += lastLineMetrics.endIndex + 1;
300             continue;
301         }
302         auto lineMetrics = info.paragraph->GetLineMetrics(lineNumber);
303         lineMetrics.startIndex += endIndex;
304         lineMetrics.endIndex += endIndex;
305         lineMetrics.lineNumber = lineNumberParam;
306         lineMetrics.y += paragraphsHeight;
307         lineMetrics.baseline += paragraphsHeight;
308         return lineMetrics;
309     }
310     return TextLineMetrics();
311 }
312 
IsIndexAtParagraphEnd(int32_t index)313 bool ParagraphManager::IsIndexAtParagraphEnd(int32_t index)
314 {
315     for (const auto& info : paragraphs_) {
316         if (info.end == index) {
317             return true;
318         }
319     }
320     return false;
321 }
322 
GetPaintRegion(RectF & boundsRect,float x,float y) const323 void ParagraphManager::GetPaintRegion(RectF& boundsRect, float x, float y) const
324 {
325     if (paragraphs_.empty()) {
326         return;
327     }
328     for (const auto& info : paragraphs_) {
329         CHECK_NULL_VOID(info.paragraph);
330         auto rect = info.paragraph->GetPaintRegion(x, y);
331         boundsRect = boundsRect.CombineRectT(rect);
332         y += info.paragraph->GetHeight();
333     }
334 }
335 
GetRectsForRange(int32_t start,int32_t end,RectHeightStyle heightStyle,RectWidthStyle widthStyle)336 std::vector<ParagraphManager::TextBox> ParagraphManager::GetRectsForRange(
337     int32_t start, int32_t end, RectHeightStyle heightStyle, RectWidthStyle widthStyle)
338 {
339     std::vector<TextBox> resultTextBoxes;
340     float y = 0.0f;
341     for (const auto& info : paragraphs_) {
342         if (info.start >= end) {
343             break;
344         }
345         int32_t relativeStart = std::max(static_cast<int32_t>(0), start - info.start);
346         int32_t relativeEnd = std::min(info.end - info.start, end - info.start);
347         if (relativeStart >= relativeEnd) {
348             y += info.paragraph->GetHeight();
349             continue;
350         }
351         std::vector<RectF> tempRects;
352         std::vector<TextDirection> tempTextDirections;
353         info.paragraph->TxtGetRectsForRange(
354             relativeStart, relativeEnd, heightStyle, widthStyle, tempRects, tempTextDirections);
355         if (tempTextDirections.size() < tempRects.size()) {
356             TAG_LOGE(AceLogTag::ACE_TEXT, "TxtGetRectsForRange failed, tempTextDirections size=%{public}zu is less "\
357                 "than tempRects size=%{public}zu", tempTextDirections.size(), tempRects.size());
358             continue;
359         }
360         for (size_t i = 0; i < tempRects.size(); ++i) {
361             tempRects[i].SetTop(tempRects[i].Top() + y);
362             resultTextBoxes.emplace_back(TextBox(tempRects[i], tempTextDirections[i]));
363         }
364         y += info.paragraph->GetHeight();
365     }
366     return resultTextBoxes;
367 }
368 
GetEllipsisTextRange()369 std::pair<size_t, size_t> ParagraphManager::GetEllipsisTextRange()
370 {
371     std::pair<size_t, size_t> range = {std::numeric_limits<size_t>::max(), 0};
372     for (auto&& info : paragraphs_) {
373         const auto& ellipsisTextRange = info.paragraph->GetEllipsisTextRange();
374         range.first = std::min(range.first, ellipsisTextRange.first);
375         range.second = std::max(range.second, ellipsisTextRange.second);
376     }
377     return range;
378 }
379 
GetRects(int32_t start,int32_t end,RectHeightPolicy rectHeightPolicy) const380 std::vector<RectF> ParagraphManager::GetRects(int32_t start, int32_t end, RectHeightPolicy rectHeightPolicy) const
381 {
382     std::vector<RectF> res;
383     float y = 0.0f;
384     for (auto&& info : paragraphs_) {
385         std::vector<RectF> rects;
386         if (info.start > end) {
387             break;
388         }
389         if (info.end > start) {
390             auto relativeStart = (start < info.start) ? 0 : start - info.start;
391             if (rectHeightPolicy == RectHeightPolicy::COVER_TEXT) {
392                 info.paragraph->GetTightRectsForRange(relativeStart, end - info.start, rects);
393             } else {
394                 info.paragraph->GetRectsForRange(relativeStart, end - info.start, rects);
395             }
396 
397             for (auto&& rect : rects) {
398                 rect.SetTop(rect.Top() + y);
399             }
400             res.insert(res.end(), rects.begin(), rects.end());
401         }
402         y += info.paragraph->GetHeight();
403     }
404     return res;
405 }
406 
GetParagraphInfo(int32_t position) const407 ParagraphManager::ParagraphInfo ParagraphManager::GetParagraphInfo(int32_t position) const
408 {
409     CHECK_EQUAL_RETURN(paragraphs_.empty(), true, {});
410     auto it = std::find_if(paragraphs_.begin(), paragraphs_.end(), [position](const ParagraphInfo& info) {
411         return (info.start <= position) && (position < info.end);
412     });
413     if (position == paragraphs_.back().end) {
414         --it;
415     }
416     CHECK_EQUAL_RETURN(it == paragraphs_.end(), true, {});
417     return (*it);
418 }
419 
GetParagraphsRects(int32_t start,int32_t end,RectHeightPolicy rectHeightPolicy) const420 std::vector<std::pair<std::vector<RectF>, TextDirection>> ParagraphManager::GetParagraphsRects(
421     int32_t start, int32_t end, RectHeightPolicy rectHeightPolicy) const
422 {
423     std::vector<std::pair<std::vector<RectF>, TextDirection>> paragraphsRects;
424     float y = 0.0f;
425     for (auto&& info : paragraphs_) {
426         if (info.start > end) {
427             break;
428         }
429         if (info.end > start) {
430             std::vector<RectF> rects;
431             auto relativeStart = (start < info.start) ? 0 : start - info.start;
432             if (rectHeightPolicy == RectHeightPolicy::COVER_TEXT) {
433                 info.paragraph->GetTightRectsForRange(relativeStart, end - info.start, rects);
434             } else {
435                 info.paragraph->GetRectsForRange(relativeStart, end - info.start, rects);
436             }
437             std::pair<std::vector<RectF>, TextDirection> paragraphRects;
438             for (auto&& rect : rects) {
439                 rect.SetTop(rect.Top() + y);
440             }
441             paragraphRects.first = rects;
442             paragraphRects.second = info.paragraphStyle.direction;
443             paragraphsRects.emplace_back(paragraphRects);
444         }
445         y += info.paragraph->GetHeight();
446     }
447     return paragraphsRects;
448 }
449 
GetTextBoxesForSelect(int32_t start,int32_t end,RectHeightPolicy rectHeightPolicy) const450 std::vector<std::pair<std::vector<RectF>, ParagraphStyle>> ParagraphManager::GetTextBoxesForSelect(
451     int32_t start, int32_t end, RectHeightPolicy rectHeightPolicy) const
452 {
453     SelectData selectData;
454     selectData.secondResult = CalcCaretMetricsByPosition(end, selectData.secondMetrics, TextAffinity::UPSTREAM);
455     std::vector<std::pair<std::vector<RectF>, ParagraphStyle>> paragraphsRects;
456     selectData.y = 0.0f;
457     for (auto&& info : paragraphs_) {
458         if (info.start > end) {
459             break;
460         }
461         CHECK_NULL_BREAK(info.paragraph);
462         if (info.end > start) {
463             std::vector<RectF> rects;
464             selectData.relativeStart = std::max(0, start - info.start);
465             selectData.relativeEnd = end - info.start;
466             selectData.paragraphSpacing = info.paragraphStyle.paragraphSpacing.ConvertToPx();
467             if (rectHeightPolicy == RectHeightPolicy::COVER_TEXT) {
468                 info.paragraph->GetTightRectsForRange(selectData.relativeStart, selectData.relativeEnd, rects);
469             } else {
470                 info.paragraph->GetRectsForRange(selectData.relativeStart, selectData.relativeEnd, rects);
471             }
472             MakeBlankLineRectsInParagraph(rects, info, selectData);
473             for (auto&& rect : rects) {
474                 rect.SetTop(rect.Top() + selectData.y);
475             }
476             paragraphsRects.emplace_back(std::make_pair(rects, info.paragraphStyle));
477         }
478         selectData.y += info.paragraph->GetHeight();
479     }
480     if (!paragraphsRects.empty()) {
481         selectData.y = 0.0f;
482         RemoveBlankLineRectByHandler(paragraphsRects.back().first, selectData);
483     }
484     return paragraphsRects;
485 }
486 
MakeBlankLineRectsInParagraph(std::vector<RectF> & result,const ParagraphInfo & info,const SelectData & selectData)487 void ParagraphManager::MakeBlankLineRectsInParagraph(std::vector<RectF>& result, const ParagraphInfo& info,
488     const SelectData& selectData)
489 {
490     const int32_t realEnd = info.end - info.start;
491     const bool isLastParagraph = (selectData.relativeEnd == 0) || (selectData.relativeEnd <= realEnd);
492     AppendParagraphSpacingBlankRect(result, selectData);
493     if (isLastParagraph && !result.empty() && IsRectOutByHandler(result.back(), selectData)) {
494         auto lastRect = result.back();
495         result.pop_back();
496         AddParagraphSpacingBlankRect(result, lastRect, selectData);
497         return;
498     }
499     CHECK_NULL_VOID(info.paragraph);
500     float height = info.paragraph->GetHeight();
501     if (Positive(selectData.paragraphSpacing) && !isLastParagraph && !result.empty() &&
502         NearZero(result.back().Width()) && selectData.relativeEnd != realEnd) {
503         result.emplace_back(RectF(0.0f, height - selectData.paragraphSpacing, 0.0f, selectData.paragraphSpacing));
504     }
505     const float lastBottom = result.empty() ? MIN_RECT_TOP : result.back().Bottom();
506     int32_t loopStart = std::min(realEnd, selectData.relativeEnd);
507     int32_t loopEnd = std::max(0, selectData.relativeStart);
508     std::vector<RectF> rects;
509     for (int32_t index = loopStart; index >= loopEnd; index--) {
510         if (GreatOrEqualCustomPrecision(lastBottom, height, MIN_RECT_PRECISION)) {
511             break;
512         }
513         CaretMetricsF caretMetrics;
514         bool res = info.paragraph->CalcCaretMetricsByPosition(index, caretMetrics, TextAffinity::UPSTREAM);
515         CHECK_NULL_BREAK(res)
516         RectF rect(caretMetrics.offset.GetX(), caretMetrics.offset.GetY(), 0.0f, caretMetrics.height);
517         if (GreatNotEqual(rect.Bottom(), height) || (isLastParagraph && IsRectOutByHandler(rect, selectData))) {
518             continue;
519         }
520         if (LessNotEqual(rect.Top(), lastBottom)) {
521             break;
522         }
523         height = rect.Top();
524         rects.emplace_back(rect);
525     }
526     std::reverse(rects.begin(), rects.end());
527     result.insert(result.end(), rects.begin(), rects.end());
528 }
529 
AppendParagraphSpacingBlankRect(std::vector<RectF> & rects,const SelectData & selectData)530 void ParagraphManager::AppendParagraphSpacingBlankRect(std::vector<RectF>& rects, const SelectData& selectData)
531 {
532     if (!Positive(selectData.paragraphSpacing) || rects.empty()) {
533         return;
534     }
535     std::vector<RectF> selectedRects = std::move(rects);
536     for (auto it = selectedRects.begin(); it != selectedRects.end(); it++) {
537         auto rect = *it;
538         if (NearZero(rect.Width()) && (it == selectedRects.begin() || !NearEqual(rect.Top(), std::prev(it)->Top()))) {
539             rect.SetHeight(rect.Height() + selectData.paragraphSpacing);
540         }
541         rects.push_back(rect);
542     }
543 }
544 
AddParagraphSpacingBlankRect(std::vector<RectF> & rects,const RectF & lastRect,const SelectData & selectData)545 void ParagraphManager::AddParagraphSpacingBlankRect(
546     std::vector<RectF>& rects, const RectF& lastRect, const SelectData& selectData)
547 {
548     if (!Positive(selectData.paragraphSpacing) || rects.empty() || NearEqual(rects.back().Top(), lastRect.Top())) {
549         return;
550     }
551     rects.emplace_back(
552         RectF(lastRect.Left(), lastRect.Top() - selectData.paragraphSpacing, 0.0f, selectData.paragraphSpacing));
553 }
554 
GetRichEditorBoxesForSelect(int32_t start,int32_t end,RectHeightPolicy rectHeightPolicy) const555 std::vector<std::pair<std::vector<RectF>, ParagraphStyle>> ParagraphManager::GetRichEditorBoxesForSelect(
556     int32_t start, int32_t end, RectHeightPolicy rectHeightPolicy) const
557 {
558     SelectData selectData;
559     selectData.secondResult = CalcCaretMetricsByPosition(end, selectData.secondMetrics, TextAffinity::DOWNSTREAM);
560     std::vector<std::pair<std::vector<RectF>, ParagraphStyle>> paragraphsRects;
561     selectData.y = 0.0f;
562     for (auto&& info : paragraphs_) {
563         if (info.start > end) {
564             break;
565         }
566         CHECK_NULL_BREAK(info.paragraph);
567         if (info.end > start) {
568             std::vector<RectF> rects;
569             selectData.relativeStart = std::max(0, start - info.start);
570             selectData.relativeEnd = end - info.start;
571             if (rectHeightPolicy == RectHeightPolicy::COVER_TEXT) {
572                 info.paragraph->GetTightRectsForRange(selectData.relativeStart, selectData.relativeEnd, rects);
573             } else {
574                 info.paragraph->GetRectsForRange(selectData.relativeStart, selectData.relativeEnd, rects);
575             }
576             MakeBlankRectsInRichEditor(rects, info, selectData);
577             for (auto&& rect : rects) {
578                 rect.SetTop(rect.Top() + selectData.y);
579             }
580             paragraphsRects.emplace_back(std::make_pair(rects, info.paragraphStyle));
581             selectData.paragraphSpacing = info.paragraphStyle.paragraphSpacing.ConvertToPx();
582         }
583         selectData.y += info.paragraph->GetHeight();
584     }
585     if (!paragraphsRects.empty()) {
586         selectData.y = 0.0f;
587         RemoveBlankLineRectByHandler(paragraphsRects.back().first, selectData);
588     }
589     return paragraphsRects;
590 }
591 
MakeBlankRectsInRichEditor(std::vector<RectF> & result,const ParagraphInfo & info,const SelectData & selectData)592 void ParagraphManager::MakeBlankRectsInRichEditor(std::vector<RectF>& result, const ParagraphInfo& info,
593     const SelectData& selectData)
594 {
595     const int32_t realEnd = info.end - info.start;
596     const bool isLastParagraph = (selectData.relativeEnd == 0) || (selectData.relativeEnd < realEnd);
597     if (isLastParagraph && !result.empty() && IsRectOutByHandler(result.back(), selectData)) {
598         result.pop_back();
599         return;
600     }
601     CHECK_NULL_VOID(info.paragraph);
602     float height = info.paragraph->GetHeight();
603     const float lastBottom = result.empty() ? MIN_RECT_TOP : result.back().Bottom();
604     int32_t loopStart = std::min(realEnd, selectData.relativeEnd);
605     int32_t loopEnd = std::max(0, selectData.relativeStart);
606     std::vector<RectF> rects;
607     for (int32_t index = loopStart; index >= loopEnd; index--) {
608         if (GreatOrEqualCustomPrecision(lastBottom, height, MIN_RECT_PRECISION)) {
609             break;
610         }
611         CaretMetricsF caretMetrics;
612         bool res = info.paragraph->CalcCaretMetricsByPosition(index, caretMetrics, TextAffinity::UPSTREAM);
613         CHECK_NULL_BREAK(res)
614         RectF rect(caretMetrics.offset.GetX(), caretMetrics.offset.GetY(), 0.0f, caretMetrics.height);
615         height  = rect.Top();
616         rects.emplace_back(rect);
617         if (auto spacing = selectData.paragraphSpacing; index == 0 && !NearZero(spacing)) {
618             rects.emplace_back(caretMetrics.offset.GetX(), caretMetrics.offset.GetY() - spacing, 0, spacing);
619         }
620     }
621     std::reverse(rects.begin(), rects.end());
622     result.insert(result.end(), rects.begin(), rects.end());
623 }
624 
RemoveBlankLineRectByHandler(std::vector<RectF> & rects,const SelectData & selectData)625 void ParagraphManager::RemoveBlankLineRectByHandler(std::vector<RectF>& rects, const SelectData& selectData)
626 {
627     while (!rects.empty()) {
628         CHECK_EQUAL_VOID(IsRectOutByHandler(rects.back(), selectData), false);
629         rects.pop_back();
630     }
631 }
632 
IsRectOutByHandler(const RectF & rect,const SelectData & selectData)633 bool ParagraphManager::IsRectOutByHandler(const RectF& rect, const SelectData& selectData)
634 {
635     CHECK_EQUAL_RETURN(NearZero(rect.Width()), false, false);
636     CHECK_EQUAL_RETURN(selectData.secondResult, false, false);
637     return GreatOrEqual(rect.Top() + selectData.y, selectData.secondMetrics.offset.GetY());
638 }
639 
IsSelectLineHeadAndUseLeadingMargin(int32_t start) const640 bool ParagraphManager::IsSelectLineHeadAndUseLeadingMargin(int32_t start) const
641 {
642     for (auto iter = paragraphs_.begin(); iter != paragraphs_.end(); iter++) {
643         auto curParagraph = *iter;
644         if (auto paragraph = curParagraph.paragraph; curParagraph.start == start && paragraph) {
645             auto leadingMargin = paragraph->GetParagraphStyle().leadingMargin;
646             CHECK_EQUAL_RETURN(leadingMargin && leadingMargin.value().IsValid(), true, true);
647         }
648         auto next = std::next(iter);
649         if (next != paragraphs_.end()) {
650             auto nextParagraph = *next;
651             if (auto paragraph = nextParagraph.paragraph; nextParagraph.start == start + 1) {
652                 auto leadingMargin = paragraph->GetParagraphStyle().leadingMargin;
653                 CHECK_EQUAL_RETURN(leadingMargin && leadingMargin.value().IsValid(), true, true);
654             }
655         }
656     }
657     return false;
658 }
659 
LayoutParagraphs(float maxWidth)660 void ParagraphManager::LayoutParagraphs(float maxWidth)
661 {
662     for (auto&& info : paragraphs_) {
663         auto paragraph = info.paragraph;
664         CHECK_NULL_CONTINUE(paragraph);
665         paragraph->Layout(maxWidth);
666     }
667 }
668 
GetPlaceholderRects() const669 std::vector<RectF> ParagraphManager::GetPlaceholderRects() const
670 {
671     std::vector<RectF> res;
672     float y = 0.0f;
673     for (auto&& info : paragraphs_) {
674         std::vector<RectF> rects;
675         info.paragraph->GetRectsForPlaceholders(rects);
676         for (auto& rect : rects) {
677             rect.SetTop(rect.Top() + y);
678         }
679         y += info.paragraph->GetHeight();
680 
681         res.insert(res.end(), rects.begin(), rects.end());
682     }
683     return res;
684 }
685 
ComputeCursorOffset(int32_t index,float & selectLineHeight,bool downStreamFirst,bool needLineHighest) const686 OffsetF ParagraphManager::ComputeCursorOffset(
687     int32_t index, float& selectLineHeight, bool downStreamFirst, bool needLineHighest) const
688 {
689     CHECK_NULL_RETURN(!paragraphs_.empty(), {});
690     auto it = paragraphs_.begin();
691     float y = 0.0f;
692     while (it != paragraphs_.end()) {
693         if (index >= it->start && index < it->end) {
694             break;
695         }
696         y += it->paragraph->GetHeight();
697         ++it;
698     }
699 
700     if (index == paragraphs_.back().end) {
701         --it;
702         y -= it->paragraph->GetHeight();
703     }
704 
705     CHECK_NULL_RETURN(it != paragraphs_.end(), OffsetF(0.0f, y));
706 
707     int32_t relativeIndex = index - it->start;
708     auto&& paragraph = it->paragraph;
709     CaretMetricsF metrics;
710     auto computeSuccess = false;
711     if (downStreamFirst) {
712         computeSuccess = paragraph->ComputeOffsetForCaretDownstream(relativeIndex, metrics, needLineHighest) ||
713                          paragraph->ComputeOffsetForCaretUpstream(relativeIndex, metrics, needLineHighest);
714     } else {
715         computeSuccess = paragraph->ComputeOffsetForCaretUpstream(relativeIndex, metrics, needLineHighest) ||
716                          paragraph->ComputeOffsetForCaretDownstream(relativeIndex, metrics, needLineHighest);
717     }
718     CHECK_NULL_RETURN(computeSuccess, OffsetF(0.0f, y));
719     selectLineHeight = metrics.height;
720     return { static_cast<float>(metrics.offset.GetX()), static_cast<float>(metrics.offset.GetY() + y) };
721 }
722 
ComputeCursorInfoByClick(int32_t index,float & selectLineHeight,const OffsetF & lastTouchOffset) const723 OffsetF ParagraphManager::ComputeCursorInfoByClick(
724     int32_t index, float& selectLineHeight, const OffsetF& lastTouchOffset) const
725 {
726     CHECK_NULL_RETURN(!paragraphs_.empty(), {});
727     auto it = paragraphs_.begin();
728     float y = 0.0f;
729     while (it != paragraphs_.end()) {
730         if (index >= it->start && index < it->end) {
731             break;
732         }
733         y += it->paragraph->GetHeight();
734         ++it;
735     }
736 
737     if (index == paragraphs_.back().end) {
738         --it;
739         y -= it->paragraph->GetHeight();
740     }
741 
742     CHECK_NULL_RETURN(it != paragraphs_.end(), OffsetF(0.0f, y));
743 
744     int32_t relativeIndex = index - it->start;
745     auto&& paragraph = it->paragraph;
746 
747     CaretMetricsF caretCaretMetric;
748     auto touchOffsetInCurrentParagraph = OffsetF(static_cast<float>(lastTouchOffset.GetX()),
749         static_cast<float>(lastTouchOffset.GetY() - y));
750     TextAffinity textAffinity;
751     paragraph->CalcCaretMetricsByPosition(relativeIndex, caretCaretMetric, touchOffsetInCurrentParagraph, textAffinity);
752     selectLineHeight = caretCaretMetric.height;
753     return { static_cast<float>(caretCaretMetric.offset.GetX()),
754         static_cast<float>(caretCaretMetric.offset.GetY() + y) };
755 }
756 
Reset()757 void ParagraphManager::Reset()
758 {
759     paragraphs_.clear();
760 }
761 
ToString() const762 std::string ParagraphManager::ParagraphInfo::ToString() const
763 {
764     return "Paragraph start: " + std::to_string(start) + ", end: " + std::to_string(end);
765 }
766 } // namespace OHOS::Ace::NG
767