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/pattern/text/text_layout_algorithm.h"
17 
18 #include <limits>
19 
20 #include "base/geometry/dimension.h"
21 #include "base/log/ace_trace.h"
22 #include "base/utils/utils.h"
23 #include "core/components/common/layout/constants.h"
24 #include "core/components/common/properties/alignment.h"
25 #include "core/components/common/properties/text_style.h"
26 #include "core/components/hyperlink/hyperlink_theme.h"
27 #include "core/components_ng/base/frame_node.h"
28 #include "core/components_ng/pattern/rich_editor/paragraph_manager.h"
29 #include "core/components_ng/pattern/text/text_layout_property.h"
30 #include "core/components_ng/pattern/text/text_pattern.h"
31 #include "core/components_ng/pattern/text_drag/text_drag_base.h"
32 #include "core/components_ng/render/font_collection.h"
33 #include "core/pipeline_ng/pipeline_context.h"
34 #include "core/text/text_emoji_processor.h"
35 
36 namespace OHOS::Ace::NG {
37 
TextLayoutAlgorithm(std::list<RefPtr<SpanItem>> spans,RefPtr<ParagraphManager> pManager,bool isSpanStringMode,bool isMarquee)38 TextLayoutAlgorithm::TextLayoutAlgorithm(
39     std::list<RefPtr<SpanItem>> spans, RefPtr<ParagraphManager> pManager, bool isSpanStringMode, bool isMarquee)
40 {
41     paragraphManager_ = pManager;
42     isSpanStringMode_ = isSpanStringMode;
43 
44     if (!isSpanStringMode) {
45         if (!spans.empty()) {
46             spans_.emplace_back(std::move(spans));
47         }
48         return;
49     }
50     if (spans.empty()) {
51         return;
52     }
53     if (isMarquee) {
54         for (const auto& span : spans) {
55             span->SetNeedRemoveNewLine(false);
56         }
57         spans_.emplace_back(std::move(spans));
58         return;
59     }
60     ConstructParagraphSpanGroup(spans);
61     if (!spans.empty()) {
62         auto maxlines = spans.front()->textLineStyle->GetMaxLines().value_or(UINT32_MAX);
63         spanStringHasMaxLines_ |= maxlines != UINT32_MAX;
64         spans_.emplace_back(std::move(spans));
65     }
66 }
67 
68 TextLayoutAlgorithm::TextLayoutAlgorithm() = default;
69 
ConstructParagraphSpanGroup(std::list<RefPtr<SpanItem>> & spans)70 void TextLayoutAlgorithm::ConstructParagraphSpanGroup(std::list<RefPtr<SpanItem>>& spans)
71 {
72     // split spans into groups by mew paragraph style
73     auto it = spans.begin();
74     ParagraphStyle pStyle;
75     GetSpanParagraphStyle(nullptr, (*it), pStyle);
76     while (it != spans.end()) {
77         auto spanItem = *it;
78         if (!spanItem) {
79             ++it;
80             continue;
81         }
82         spanItem->SetNeedRemoveNewLine(false);
83         auto wContent = StringUtils::ToWstring(spanItem->content);
84         if (wContent.back() == L'\n') {
85             if (std::next(it) == spans.end()) {
86                 break;
87             }
88             auto next = *(std::next(it));
89             ParagraphStyle nextSpanParagraphStyle;
90             if (next) {
91                 GetSpanParagraphStyle(nullptr, next, nextSpanParagraphStyle);
92             } else {
93                 break;
94             }
95             if (pStyle != nextSpanParagraphStyle ||
96                 (pStyle.leadingMargin.has_value() && pStyle.leadingMargin->pixmap) || Positive(pStyle.indent.Value()) ||
97                 pStyle.maxLines != UINT32_MAX) {
98                 std::list<RefPtr<SpanItem>> newGroup;
99                 spanItem->SetNeedRemoveNewLine(true);
100                 newGroup.splice(newGroup.begin(), spans, spans.begin(), std::next(it));
101                 spanStringHasMaxLines_ |= pStyle.maxLines != UINT32_MAX;
102                 spans_.emplace_back(std::move(newGroup));
103                 it = spans.begin();
104                 pStyle = nextSpanParagraphStyle;
105                 continue;
106             }
107         }
108         ++it;
109     }
110 }
111 
OnReset()112 void TextLayoutAlgorithm::OnReset() {}
113 
MeasureContent(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)114 std::optional<SizeF> TextLayoutAlgorithm::MeasureContent(
115     const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
116 {
117     auto host = layoutWrapper->GetHostNode();
118     CHECK_NULL_RETURN(host, std::nullopt);
119     ACE_SCOPED_TRACE("TextLayoutAlgorithm::MeasureContent[id:%d]", host->GetId());
120     TextStyle textStyle;
121     ConstructTextStyles(contentConstraint, layoutWrapper, textStyle);
122     auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
123     CHECK_NULL_RETURN(textLayoutProperty, std::nullopt);
124     if (textStyle.GetTextOverflow() == TextOverflow::MARQUEE) { // create a paragraph with all text in 1 line
125         isMarquee_ = true;
126         return BuildTextRaceParagraph(textStyle, textLayoutProperty, contentConstraint, layoutWrapper);
127     }
128     if (isSpanStringMode_) {
129         if (spanStringHasMaxLines_) {
130             textStyle.SetMaxLines(UINT32_MAX);
131         }
132         textStyle_ = textStyle;
133         BuildParagraph(textStyle, textLayoutProperty, contentConstraint, layoutWrapper);
134     } else {
135         textStyle_ = textStyle;
136         if (!AddPropertiesAndAnimations(textStyle, textLayoutProperty, contentConstraint, layoutWrapper)) {
137             return std::nullopt;
138         }
139     }
140     textStyle_ = textStyle;
141     baselineOffset_ = textStyle.GetBaselineOffset().ConvertToPxDistribute(
142         textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
143     if (NearZero(contentConstraint.maxSize.Height()) || NearZero(contentConstraint.maxSize.Width())) {
144         return SizeF {};
145     }
146     CHECK_NULL_RETURN(paragraphManager_, std::nullopt);
147     auto height = paragraphManager_->GetHeight();
148     auto maxWidth = paragraphManager_->GetMaxWidth();
149     auto longestLine = paragraphManager_->GetLongestLine();
150     auto heightFinal = static_cast<float>(height + std::fabs(baselineOffset_));
151     if (contentConstraint.selfIdealSize.Height().has_value()) {
152         heightFinal = std::min(heightFinal, contentConstraint.selfIdealSize.Height().value());
153     } else {
154         heightFinal = std::min(heightFinal, contentConstraint.maxSize.Height());
155     }
156     if (SystemProperties::GetDebugEnabled()) {
157         TAG_LOGI(AceLogTag::ACE_TEXT, "MeasureContent[id:%d][w:%f][h:%f][lineW:%f][height:%f]", host->GetId(), maxWidth,
158             heightFinal, longestLine, height);
159     }
160     if (host->GetTag() == V2::TEXT_ETS_TAG && textLayoutProperty->GetContent().value_or("").empty() &&
161         NonPositive(longestLine)) {
162         ACE_SCOPED_TRACE("TextHeightFinal [%f], TextContentWidth [%f], FontSize [%lf]", heightFinal, maxWidth,
163             textStyle.GetFontSize().ConvertToPxDistribute(
164                 textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale()));
165         return SizeF {};
166     }
167     return SizeF(maxWidth, heightFinal);
168 }
169 
AddPropertiesAndAnimations(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & textLayoutProperty,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)170 bool TextLayoutAlgorithm::AddPropertiesAndAnimations(TextStyle& textStyle,
171     const RefPtr<TextLayoutProperty>& textLayoutProperty, const LayoutConstraintF& contentConstraint,
172     LayoutWrapper* layoutWrapper)
173 {
174     bool result = false;
175     switch (textLayoutProperty->GetHeightAdaptivePolicyValue(TextHeightAdaptivePolicy::MAX_LINES_FIRST)) {
176         case TextHeightAdaptivePolicy::MAX_LINES_FIRST:
177             result = BuildParagraph(textStyle, textLayoutProperty, contentConstraint, layoutWrapper);
178             break;
179         case TextHeightAdaptivePolicy::MIN_FONT_SIZE_FIRST:
180             result = BuildParagraphAdaptUseMinFontSize(textStyle, textLayoutProperty, contentConstraint, layoutWrapper);
181             break;
182         case TextHeightAdaptivePolicy::LAYOUT_CONSTRAINT_FIRST:
183             result =
184                 BuildParagraphAdaptUseLayoutConstraint(textStyle, textLayoutProperty, contentConstraint, layoutWrapper);
185             break;
186         default:
187             break;
188     }
189     return result;
190 }
191 
UpdateParagraphForAISpan(const TextStyle & textStyle,LayoutWrapper * layoutWrapper,const RefPtr<Paragraph> & paragraph)192 void TextLayoutAlgorithm::UpdateParagraphForAISpan(
193     const TextStyle& textStyle, LayoutWrapper* layoutWrapper, const RefPtr<Paragraph>& paragraph)
194 {
195     CHECK_NULL_VOID(layoutWrapper);
196     auto layoutProperty = layoutWrapper->GetLayoutProperty();
197     CHECK_NULL_VOID(layoutProperty);
198     auto frameNode = layoutWrapper->GetHostNode();
199     CHECK_NULL_VOID(frameNode);
200     auto pattern = frameNode->GetPattern<TextPattern>();
201     CHECK_NULL_VOID(pattern);
202     auto textForAI = pattern->GetTextForAI();
203     auto wTextForAI = StringUtils::ToWstring(textForAI);
204     int32_t wTextForAILength = static_cast<int32_t>(wTextForAI.length());
205     int32_t preEnd = 0;
206     DragSpanPosition dragSpanPosition;
207     dragSpanPosition.dragStart = pattern->GetRecoverStart();
208     dragSpanPosition.dragEnd = pattern->GetRecoverEnd();
209     bool isDragging = pattern->IsDragging();
210     TextStyle aiSpanStyle = textStyle;
211     pattern->ModifyAISpanStyle(aiSpanStyle);
212     for (auto kv : pattern->GetAISpanMap()) {
213         if (preEnd >= wTextForAILength) {
214             break;
215         }
216         auto aiSpan = kv.second;
217         if (aiSpan.start < preEnd) {
218             TAG_LOGI(AceLogTag::ACE_TEXT, "Error prediction");
219             continue;
220         }
221         if (preEnd < aiSpan.start) {
222             dragSpanPosition.spanStart = preEnd;
223             dragSpanPosition.spanEnd = aiSpan.start;
224             GrayDisplayAISpan(dragSpanPosition, wTextForAI, textStyle, isDragging, paragraph);
225         }
226         preEnd = aiSpan.end;
227         dragSpanPosition.spanStart = aiSpan.start;
228         dragSpanPosition.spanEnd = aiSpan.end;
229         GrayDisplayAISpan(dragSpanPosition, wTextForAI, aiSpanStyle, isDragging, paragraph);
230     }
231     if (preEnd < wTextForAILength) {
232         dragSpanPosition.spanStart = preEnd;
233         dragSpanPosition.spanEnd = wTextForAILength;
234         GrayDisplayAISpan(dragSpanPosition, wTextForAI, textStyle, isDragging, paragraph);
235     }
236 }
237 
GrayDisplayAISpan(const DragSpanPosition & dragSpanPosition,const std::wstring wTextForAI,const TextStyle & textStyle,bool isDragging,const RefPtr<Paragraph> & paragraph)238 void TextLayoutAlgorithm::GrayDisplayAISpan(const DragSpanPosition& dragSpanPosition, const std::wstring wTextForAI,
239     const TextStyle& textStyle, bool isDragging, const RefPtr<Paragraph>& paragraph)
240 {
241     int32_t dragStart = dragSpanPosition.dragStart;
242     int32_t dragEnd = dragSpanPosition.dragEnd;
243     int32_t spanStart = dragSpanPosition.spanStart;
244     int32_t spanEnd = dragSpanPosition.spanEnd;
245     std::vector<std::string> contents = {};
246     std::string firstParagraph = "";
247     std::string secondParagraph = "";
248     std::string thirdParagraph = "";
249     if (dragStart > spanEnd || dragEnd < spanStart || !isDragging) {
250         firstParagraph = StringOutBoundProtection(spanStart, spanEnd - spanStart, wTextForAI);
251     } else if (spanStart <= dragStart && spanEnd >= dragStart && spanEnd <= dragEnd) {
252         firstParagraph = StringOutBoundProtection(spanStart, dragStart - spanStart, wTextForAI);
253         secondParagraph = StringOutBoundProtection(dragStart, spanEnd - dragStart, wTextForAI);
254     } else if (spanStart >= dragStart && spanEnd <= dragEnd) {
255         secondParagraph = StringOutBoundProtection(spanStart, spanEnd - spanStart, wTextForAI);
256     } else if (spanStart <= dragStart && spanEnd >= dragEnd) {
257         firstParagraph = StringOutBoundProtection(spanStart, dragStart - spanStart, wTextForAI);
258         secondParagraph = StringOutBoundProtection(dragStart, dragEnd - dragStart, wTextForAI);
259         thirdParagraph = StringOutBoundProtection(dragEnd, spanEnd - dragEnd, wTextForAI);
260     } else {
261         secondParagraph = StringOutBoundProtection(spanStart, dragEnd - spanStart, wTextForAI);
262         thirdParagraph = StringOutBoundProtection(dragEnd, spanEnd - dragEnd, wTextForAI);
263     }
264     contents = { firstParagraph, secondParagraph, thirdParagraph };
265     CreateParagraphDrag(textStyle, contents, paragraph);
266 }
267 
StringOutBoundProtection(int32_t position,int32_t length,std::wstring wTextForAI)268 std::string TextLayoutAlgorithm::StringOutBoundProtection(int32_t position, int32_t length, std::wstring wTextForAI)
269 {
270     int32_t wTextForAILength = static_cast<int32_t>(wTextForAI.length());
271     if (position >= wTextForAILength || length > wTextForAILength - position) {
272         return "";
273     } else {
274         return StringUtils::ToString(wTextForAI.substr(position, length));
275     }
276 }
277 
CreateParagraph(const TextStyle & textStyle,std::string content,LayoutWrapper * layoutWrapper,double maxWidth)278 bool TextLayoutAlgorithm::CreateParagraph(
279     const TextStyle& textStyle, std::string content, LayoutWrapper* layoutWrapper, double maxWidth)
280 {
281     if (!paragraphManager_) {
282         paragraphManager_ = AceType::MakeRefPtr<ParagraphManager>();
283     }
284     paragraphManager_->Reset();
285     auto frameNode = layoutWrapper->GetHostNode();
286     CHECK_NULL_RETURN(frameNode, false);
287     auto pattern = frameNode->GetPattern<TextPattern>();
288     CHECK_NULL_RETURN(pattern, false);
289     pattern->ClearCustomSpanPlaceholderInfo();
290     if (pattern->IsSensitiveEnalbe()) {
291         UpdateSensitiveContent(content);
292     }
293     auto useExternalParagraph = pattern->GetExternalParagraph() && !pattern->NeedShowAIDetect();
294     auto externalParagraphStyle = pattern->GetExternalParagraphStyle();
295     auto paraStyle = GetParagraphStyle(textStyle, content, layoutWrapper);
296     if (pattern->GetExternalParagraph()) {
297         if (!useExternalParagraph && externalParagraphStyle) {
298             paraStyle = externalParagraphStyle.value();
299         }
300     }
301     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) || isSpanStringMode_) {
302         paraStyle.fontSize = textStyle.GetFontSize().ConvertToPxDistribute(
303             textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
304     }
305     paraStyle.leadingMarginAlign = Alignment::CENTER;
306     // SymbolGlyph
307     if (frameNode->GetTag() == V2::SYMBOL_ETS_TAG) {
308         return UpdateSymbolTextStyle(textStyle, paraStyle, layoutWrapper, frameNode);
309     }
310     if (spans_.empty() || useExternalParagraph) {
311         // only use for text.
312         return UpdateSingleParagraph(layoutWrapper, paraStyle, textStyle, content, maxWidth);
313     } else {
314         return UpdateParagraphBySpan(layoutWrapper, paraStyle, maxWidth, textStyle);
315     }
316 }
317 
UpdateSymbolTextStyle(const TextStyle & textStyle,const ParagraphStyle & paraStyle,LayoutWrapper * layoutWrapper,RefPtr<FrameNode> & frameNode)318 bool TextLayoutAlgorithm::UpdateSymbolTextStyle(const TextStyle& textStyle, const ParagraphStyle& paraStyle,
319     LayoutWrapper* layoutWrapper, RefPtr<FrameNode>& frameNode)
320 {
321     auto &&paragraph = Paragraph::Create(paraStyle, FontCollection::Current());
322     CHECK_NULL_RETURN(paragraph, false);
323     auto layoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
324     CHECK_NULL_RETURN(layoutProperty, false);
325     auto symbolSourceInfo = layoutProperty->GetSymbolSourceInfo();
326     CHECK_NULL_RETURN(symbolSourceInfo, false);
327     TextStyle symbolTextStyle = textStyle;
328     symbolTextStyle.isSymbolGlyph_ = true;
329     symbolTextStyle.SetRenderStrategy(
330         symbolTextStyle.GetRenderStrategy() < 0 ? 0 : symbolTextStyle.GetRenderStrategy());
331     symbolTextStyle.SetEffectStrategy(
332         symbolTextStyle.GetEffectStrategy() < 0 ? 0 : symbolTextStyle.GetEffectStrategy());
333     symbolTextStyle.SetFontFamilies({ "HM Symbol" });
334     paragraph->PushStyle(symbolTextStyle);
335     if (symbolTextStyle.GetSymbolEffectOptions().has_value()) {
336         auto symbolEffectOptions = layoutProperty->GetSymbolEffectOptionsValue(SymbolEffectOptions());
337         symbolEffectOptions.Reset();
338         layoutProperty->UpdateSymbolEffectOptions(symbolEffectOptions);
339         if (symbolTextStyle.GetSymbolEffectOptions().has_value()) {
340             auto symboloptiOns = symbolTextStyle.GetSymbolEffectOptions().value();
341             symboloptiOns.Reset();
342         }
343     }
344     paragraph->AddSymbol(symbolSourceInfo->GetUnicode());
345     paragraph->PopStyle();
346     paragraph->Build();
347     paragraph->SetParagraphSymbolAnimation(frameNode);
348     paragraphManager_->AddParagraph({ .paragraph = paragraph, .paragraphStyle = paraStyle, .start = 0, .end = 2 });
349     return true;
350 }
351 
CreateParagraphDrag(const TextStyle & textStyle,const std::vector<std::string> & contents,const RefPtr<Paragraph> & paragraph)352 void TextLayoutAlgorithm::CreateParagraphDrag(
353     const TextStyle& textStyle, const std::vector<std::string>& contents, const RefPtr<Paragraph>& paragraph)
354 {
355     TextStyle dragTextStyle = textStyle;
356     Color color = textStyle.GetTextColor().ChangeAlpha(DRAGGED_TEXT_TRANSPARENCY);
357     dragTextStyle.SetTextColor(color);
358     Color textDecorationColor = textStyle.GetTextDecorationColor().ChangeAlpha(DRAGGED_TEXT_TRANSPARENCY);
359     dragTextStyle.SetTextDecorationColor(textDecorationColor);
360     std::vector<TextStyle> textStyles { textStyle, dragTextStyle, textStyle };
361 
362     CHECK_NULL_VOID(paragraph);
363     for (size_t i = 0; i < contents.size(); i++) {
364         std::string splitStr = contents[i];
365         if (splitStr.empty()) {
366             continue;
367         }
368         auto& style = textStyles[i];
369         paragraph->PushStyle(style);
370         StringUtils::TransformStrCase(splitStr, static_cast<int32_t>(style.GetTextCase()));
371         paragraph->AddText(StringUtils::Str8ToStr16(splitStr));
372         paragraph->PopStyle();
373     }
374 }
375 
CreateParagraphAndLayout(const TextStyle & textStyle,const std::string & content,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper,bool needLayout)376 bool TextLayoutAlgorithm::CreateParagraphAndLayout(const TextStyle& textStyle, const std::string& content,
377     const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper, bool needLayout)
378 {
379     auto maxSize = MultipleParagraphLayoutAlgorithm::GetMaxMeasureSize(contentConstraint);
380     ACE_TEXT_SCOPED_TRACE("CreateParagraphAndLayout[maxSize:%s][Len:%d]", maxSize.ToString().c_str(),
381         static_cast<int32_t>(content.length()));
382     if (!CreateParagraph(textStyle, content, layoutWrapper, maxSize.Width())) {
383         return false;
384     }
385     CHECK_NULL_RETURN(paragraphManager_, false);
386     auto paragraphInfo = paragraphManager_->GetParagraphs();
387     for (auto pIter = paragraphInfo.begin(); pIter != paragraphInfo.end(); pIter++) {
388         auto paragraph = pIter->paragraph;
389         CHECK_NULL_RETURN(paragraph, false);
390         paragraph->Layout(maxSize.Width());
391     }
392     return true;
393 }
394 
GetContentOffset(LayoutWrapper * layoutWrapper)395 OffsetF TextLayoutAlgorithm::GetContentOffset(LayoutWrapper* layoutWrapper)
396 {
397     return SetContentOffset(layoutWrapper);
398 }
399 
AdaptMinTextSize(TextStyle & textStyle,const std::string & content,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)400 bool TextLayoutAlgorithm::AdaptMinTextSize(TextStyle& textStyle, const std::string& content,
401     const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
402 {
403     ACE_TEXT_SCOPED_TRACE("TextLayoutAlgorithm::AdaptMinTextSize[Length:%d]", static_cast<int32_t>(content.length()));
404     double maxFontSize = 0.0;
405     double minFontSize = 0.0;
406     auto pipeline = PipelineContext::GetCurrentContextSafely();
407     CHECK_NULL_RETURN(pipeline, false);
408     auto maxFontSizeDimension = textStyle.GetAdaptMaxFontSize();
409     maxFontSize = maxFontSizeDimension.ConvertToPxDistribute(
410         textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
411     minFontSize = textStyle.GetAdaptMinFontSize().ConvertToPxDistribute(
412         textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
413     if (LessNotEqual(maxFontSize, minFontSize) || LessOrEqual(minFontSize, 0.0)) {
414         if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) {
415             TAG_LOGE(AceLogTag::ACE_TEXT, "create paragraph error");
416             return false;
417         }
418         return true;
419     }
420     auto adaptUnit = maxFontSizeDimension.GetAdaptDimensionUnit(textStyle.GetAdaptMinFontSize());
421     Dimension step(1.0f, adaptUnit);
422     if (GreatNotEqual(textStyle.GetAdaptFontSizeStep().Value(), 0.0)) {
423         step = textStyle.GetAdaptFontSizeStep();
424     }
425     double stepSize =
426         step.ConvertToPxDistribute(textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
427     auto maxSize = MultipleParagraphLayoutAlgorithm::GetMaxMeasureSize(contentConstraint);
428     while (GreatOrEqual(maxFontSize, minFontSize)) {
429         textStyle.SetFontSize(Dimension(maxFontSize));
430         if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) {
431             TAG_LOGE(AceLogTag::ACE_TEXT, "create paragraph error");
432             return false;
433         }
434         if (!DidExceedMaxLines(maxSize)) {
435             break;
436         }
437         bool isEqual = maxFontSize == minFontSize;
438         maxFontSize -= stepSize;
439         if (LessNotEqual(maxFontSize, minFontSize) && !isEqual) {
440             maxFontSize = minFontSize;
441         }
442     }
443     return true;
444 }
445 
GetBaselineOffset() const446 float TextLayoutAlgorithm::GetBaselineOffset() const
447 {
448     return baselineOffset_;
449 }
450 
UpdateSingleParagraph(LayoutWrapper * layoutWrapper,ParagraphStyle paraStyle,const TextStyle & textStyle,const std::string & content,double maxWidth)451 bool TextLayoutAlgorithm::UpdateSingleParagraph(LayoutWrapper* layoutWrapper, ParagraphStyle paraStyle,
452     const TextStyle& textStyle, const std::string& content, double maxWidth)
453 {
454     auto host = layoutWrapper->GetHostNode();
455     CHECK_NULL_RETURN(host, false);
456     ACE_TEXT_SCOPED_TRACE("TextLayoutAlgorithm::UpdateSingleParagraph[id:%d][length:%d][w:%f]", host->GetId(),
457         static_cast<int32_t>(content.length()), maxWidth);
458     auto pattern = host->GetPattern<TextPattern>();
459     CHECK_NULL_RETURN(pattern, false);
460     auto externalParagraph = pattern->GetExternalParagraph();
461     RefPtr<Paragraph> paragraph;
462     if (externalParagraph) {
463         paragraph = Paragraph::Create(externalParagraph.value());
464     } else {
465         paragraph = Paragraph::Create(paraStyle, FontCollection::Current());
466     }
467     CHECK_NULL_RETURN(paragraph, false);
468     auto textStyleTmp = textStyle;
469     textStyleTmp.ResetTextBaseline();
470     paragraph->PushStyle(textStyleTmp);
471     if (pattern->NeedShowAIDetect()) {
472         UpdateParagraphForAISpan(textStyle, layoutWrapper, paragraph);
473     } else {
474         if (pattern->IsDragging()) {
475             auto dragContents = pattern->GetDragContents();
476             CreateParagraphDrag(textStyle, dragContents, paragraph);
477         } else {
478             auto value = content;
479             StringUtils::TransformStrCase(value, static_cast<int32_t>(textStyle.GetTextCase()));
480             std::u16string result = StringUtils::Str8ToStr16(value);
481             if (result.length() == 0 && value.length() != 0) {
482                 value = TextEmojiProcessor::ConvertU8stringUnpairedSurrogates(value);
483                 result = StringUtils::Str8ToStr16(value);
484             }
485             paragraph->AddText(result);
486         }
487     }
488     paragraph->Build();
489     ApplyIndent(paraStyle, paragraph, maxWidth, textStyle);
490     paragraphManager_->AddParagraph({ .paragraph = paragraph,
491         .paragraphStyle = paraStyle,
492         .start = 0,
493         .end = StringUtils::Str8ToStr16(content).length() });
494     return true;
495 }
496 
BuildParagraph(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)497 bool TextLayoutAlgorithm::BuildParagraph(TextStyle& textStyle, const RefPtr<TextLayoutProperty>& layoutProperty,
498     const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
499 {
500     auto host = layoutWrapper->GetHostNode();
501     CHECK_NULL_RETURN(host, false);
502     auto pattern = host->GetPattern<TextPattern>();
503     CHECK_NULL_RETURN(pattern, false);
504     pattern->DumpRecord(",BuildParagraph id:" + std::to_string(host->GetId()));
505     if (!textStyle.GetAdaptTextSize() || !spans_.empty()) {
506         if (!CreateParagraphAndLayout(
507                 textStyle, layoutProperty->GetContent().value_or(""), contentConstraint, layoutWrapper)) {
508             TAG_LOGE(AceLogTag::ACE_TEXT, "create paragraph error");
509             return false;
510         }
511     } else {
512         if (!AdaptMinTextSize(textStyle, layoutProperty->GetContent().value_or(""), contentConstraint, layoutWrapper)) {
513             return false;
514         }
515     }
516     return ParagraphReLayout(contentConstraint);
517 }
518 
BuildParagraphAdaptUseMinFontSize(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)519 bool TextLayoutAlgorithm::BuildParagraphAdaptUseMinFontSize(TextStyle& textStyle,
520     const RefPtr<TextLayoutProperty>& layoutProperty, const LayoutConstraintF& contentConstraint,
521     LayoutWrapper* layoutWrapper)
522 {
523     if (!AdaptMaxTextSize(textStyle, layoutProperty->GetContent().value_or(""), contentConstraint, layoutWrapper)) {
524         return false;
525     }
526     auto paragraph = GetSingleParagraph();
527     CHECK_NULL_RETURN(paragraph, false);
528     // Confirmed specification: The width of the text paragraph covers the width of the component, so this code is
529     // generally not allowed to be modified
530     if (!contentConstraint.selfIdealSize.Width()) {
531         float paragraphNewWidth = std::min(std::min(paragraph->GetTextWidth(), paragraph->GetMaxWidth()),
532             MultipleParagraphLayoutAlgorithm::GetMaxMeasureSize(contentConstraint).Width());
533         paragraphNewWidth =
534             std::clamp(paragraphNewWidth, contentConstraint.minSize.Width(), contentConstraint.maxSize.Width());
535         if (!NearEqual(paragraphNewWidth, paragraph->GetMaxWidth())) {
536             paragraph->Layout(std::ceil(paragraphNewWidth));
537         }
538     }
539 
540     return true;
541 }
542 
BuildParagraphAdaptUseLayoutConstraint(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)543 bool TextLayoutAlgorithm::BuildParagraphAdaptUseLayoutConstraint(TextStyle& textStyle,
544     const RefPtr<TextLayoutProperty>& layoutProperty, const LayoutConstraintF& contentConstraint,
545     LayoutWrapper* layoutWrapper)
546 {
547     // Create the paragraph and obtain the height.
548     if (!BuildParagraph(textStyle, layoutProperty, contentConstraint, layoutWrapper)) {
549         return false;
550     }
551 
552     auto paragraph = GetSingleParagraph();
553     CHECK_NULL_RETURN(paragraph, false);
554     auto lineCount = static_cast<uint32_t>(paragraph->GetLineCount());
555     lineCount = std::max(std::min(textStyle.GetMaxLines(), lineCount), static_cast<uint32_t>(0));
556     textStyle.SetMaxLines(lineCount);
557     textStyle.DisableAdaptTextSize();
558 
559     auto height = static_cast<float>(paragraph->GetHeight());
560     while (GreatNotEqual(height, contentConstraint.maxSize.Height())) {
561         auto maxLines = textStyle.GetMaxLines();
562         if (maxLines == 0) {
563             break;
564         } else {
565             maxLines = textStyle.GetMaxLines() - 1;
566             textStyle.SetMaxLines(maxLines);
567         }
568         if (!BuildParagraph(textStyle, layoutProperty, contentConstraint, layoutWrapper)) {
569             return false;
570         }
571         paragraph = GetSingleParagraph();
572         height = static_cast<float>(paragraph->GetHeight());
573     }
574     return true;
575 }
576 
BuildTextRaceParagraph(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)577 std::optional<SizeF> TextLayoutAlgorithm::BuildTextRaceParagraph(TextStyle& textStyle,
578     const RefPtr<TextLayoutProperty>& layoutProperty, const LayoutConstraintF& contentConstraint,
579     LayoutWrapper* layoutWrapper)
580 {
581     // create a paragraph with all text in 1 line
582     textStyle.SetTextOverflow(TextOverflow::CLIP);
583     textStyle.SetMaxLines(1);
584     textStyle.SetTextIndent(Dimension(0.0f));
585     std::string content = layoutProperty->GetContent().value_or("");
586     std::replace(content.begin(), content.end(), '\n', ' ');
587     if (!textStyle.GetAdaptTextSize()) {
588         if (!CreateParagraph(textStyle, content, layoutWrapper)) {
589             return std::nullopt;
590         }
591     } else {
592         if (!AdaptMinTextSize(textStyle, content, contentConstraint, layoutWrapper)) {
593             return std::nullopt;
594         }
595     }
596 
597     textStyle_ = textStyle;
598     auto paragraph = GetSingleParagraph();
599     // layout the paragraph to the width of text
600     paragraph->Layout(std::numeric_limits<float>::max());
601     float paragraphWidth = paragraph->GetLongestLineWithIndent();
602     if (contentConstraint.selfIdealSize.Width().has_value()) {
603         paragraphWidth = std::max(contentConstraint.selfIdealSize.Width().value(), paragraphWidth);
604     } else {
605         paragraphWidth = std::max(contentConstraint.minSize.Width(), paragraphWidth);
606     }
607     paragraphWidth = std::ceil(paragraphWidth);
608     paragraph->Layout(paragraphWidth);
609 
610     auto pipeline = PipelineContext::GetCurrentContextSafely();
611     // calculate the content size
612     auto height = static_cast<float>(paragraph->GetHeight());
613     baselineOffset_ = static_cast<float>(
614         layoutProperty->GetBaselineOffsetValue(Dimension())
615             .ConvertToPxDistribute(textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale()));
616     float heightFinal =
617         std::min(static_cast<float>(height + std::fabs(baselineOffset_)), contentConstraint.maxSize.Height());
618 
619     float widthFinal = paragraphWidth;
620     if (contentConstraint.selfIdealSize.Width().has_value()) {
621         if (contentConstraint.selfIdealSize.Width().value() < paragraphWidth) {
622             widthFinal = contentConstraint.selfIdealSize.Width().value();
623         }
624     } else if (contentConstraint.maxSize.Width() < paragraphWidth) {
625         widthFinal = contentConstraint.maxSize.Width();
626     }
627     return SizeF(widthFinal, heightFinal);
628 }
629 
AdaptMaxTextSize(TextStyle & textStyle,const std::string & content,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)630 bool TextLayoutAlgorithm::AdaptMaxTextSize(TextStyle& textStyle, const std::string& content,
631     const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
632 {
633     constexpr Dimension ADAPT_UNIT = 1.0_fp;
634     auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
635     CHECK_NULL_RETURN(textLayoutProperty, false);
636     auto step = textLayoutProperty->GetAdaptFontSizeStepValue(ADAPT_UNIT);
637     return AdaptMaxFontSize(textStyle, content, step, contentConstraint, layoutWrapper);
638 }
639 
UpdateSensitiveContent(std::string & content)640 void TextLayoutAlgorithm::UpdateSensitiveContent(std::string& content)
641 {
642     auto wContent = StringUtils::ToWstring(content);
643     std::replace_if(
644         wContent.begin(), wContent.end(),
645         [](wchar_t ch) {
646             return ch != L'\n';
647         }, L'-');
648     content = StringUtils::ToString(wContent);
649 }
650 
GetTextStyle() const651 std::optional<TextStyle> TextLayoutAlgorithm::GetTextStyle() const
652 {
653     return textStyle_;
654 }
655 
GetLineCount() const656 size_t TextLayoutAlgorithm::GetLineCount() const
657 {
658     size_t count = 0;
659     CHECK_NULL_RETURN(paragraphManager_, 0);
660     auto paragraphInfo = paragraphManager_->GetParagraphs();
661     for (auto pIter = paragraphInfo.begin(); pIter != paragraphInfo.end(); pIter++) {
662         auto paragraph = pIter->paragraph;
663         CHECK_NULL_RETURN(paragraph, 0);
664         count += paragraph->GetLineCount();
665     }
666     return count;
667 }
668 } // namespace OHOS::Ace::NG
669