• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/text/text_layout_algorithm.h"
17 
18 #include "base/geometry/dimension.h"
19 #include "base/log/ace_trace.h"
20 #include "base/utils/utf_helper.h"
21 #include "core/components/common/properties/alignment.h"
22 #include "core/components/common/properties/text_style.h"
23 #include "core/components/hyperlink/hyperlink_theme.h"
24 #include "core/components/text/text_theme.h"
25 #include "core/components_ng/pattern/text/text_base.h"
26 #include "core/components_ng/pattern/text/text_pattern.h"
27 #include "core/components_ng/pattern/text/paragraph_util.h"
28 #ifdef ENABLE_ROSEN_BACKEND
29 #include "render_service_client/core/ui/rs_ui_director.h"
30 #endif
31 
32 namespace OHOS::Ace::NG {
33 namespace {
34 constexpr int32_t HUNDRED = 100;
35 constexpr int32_t TWENTY = 20;
36 
GetAdaptedMaxLines(const TextStyle & textStyle,const LayoutConstraintF & contentConstraint)37 uint32_t GetAdaptedMaxLines(const TextStyle& textStyle, const LayoutConstraintF& contentConstraint)
38 {
39     double minTextSizeHeight = textStyle.GetAdaptMinFontSize().ConvertToPxDistribute(
40         textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
41     if (LessOrEqual(minTextSizeHeight, 0.0)) {
42         minTextSizeHeight = textStyle.GetFontSize().ConvertToPxDistribute(
43             textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
44     }
45     double lineHeight = minTextSizeHeight;
46     if (textStyle.HasHeightOverride()) {
47         lineHeight = textStyle.GetLineHeight().Unit() == DimensionUnit::PERCENT
48                             ? textStyle.GetLineHeight().ConvertToPxWithSize(contentConstraint.maxSize.Height())
49                             : textStyle.GetLineHeight().ConvertToPxDistribute(
50                                 textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
51     }
52     // plus extraLine to ensure maxlines -1 is the next maxline to try for layout
53     uint32_t maxLines = contentConstraint.maxSize.Height() / (lineHeight) + 1;
54     return std::max(maxLines, static_cast<uint32_t>(0));
55 }
56 }; // namespace
57 
TextLayoutAlgorithm(std::list<RefPtr<SpanItem>> spans,RefPtr<ParagraphManager> pManager,bool isSpanStringMode,const TextStyle & textStyle,bool isMarquee)58 TextLayoutAlgorithm::TextLayoutAlgorithm(
59     std::list<RefPtr<SpanItem>> spans, RefPtr<ParagraphManager> pManager, bool isSpanStringMode,
60     const TextStyle& textStyle, bool isMarquee)
61 {
62     if (SystemProperties::GetTextTraceEnabled()) {
63         ACE_TEXT_SCOPED_TRACE("TextLayoutAlgorithm::TextLayoutAlgorithm[styleUid:%d][isSpanStringMode:%d][size:%d]",
64             textStyle.GetTextStyleUid(), isSpanStringMode, static_cast<int32_t>(spans.size()));
65     }
66     paragraphManager_ = pManager;
67     isSpanStringMode_ = isSpanStringMode;
68     textStyle_ = textStyle;
69 
70     if (!isSpanStringMode) {
71         if (!spans.empty()) {
72             spans_.emplace_back(std::move(spans));
73         }
74         return;
75     }
76     if (spans.empty()) {
77         return;
78     }
79     if (isMarquee) {
80         for (const auto& span : spans) {
81             span->SetNeedRemoveNewLine(false);
82         }
83         spans_.emplace_back(std::move(spans));
84         return;
85     }
86     ParagraphUtil::ConstructParagraphSpanGroup(spans, spans_, spanStringHasMaxLines_);
87 }
88 
89 TextLayoutAlgorithm::TextLayoutAlgorithm() = default;
90 
OnReset()91 void TextLayoutAlgorithm::OnReset() {}
92 
MeasureContent(const LayoutConstraintF & constraint,LayoutWrapper * layoutWrapper)93 std::optional<SizeF> TextLayoutAlgorithm::MeasureContent(
94     const LayoutConstraintF& constraint, LayoutWrapper* layoutWrapper)
95 {
96     auto host = layoutWrapper->GetHostNode();
97     CHECK_NULL_RETURN(host, std::nullopt);
98     auto pattern = host->GetPattern<TextPattern>();
99     CHECK_NULL_RETURN(pattern, std::nullopt);
100     auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
101     CHECK_NULL_RETURN(textLayoutProperty, std::nullopt);
102     auto contentConstraint = CalcContentConstraint(constraint, layoutWrapper);
103     ConstructTextStyles(contentConstraint, layoutWrapper, textStyle_);
104     if (isSpanStringMode_ && spanStringHasMaxLines_) {
105         textStyle_.SetMaxLines(UINT32_MAX);
106     }
107     // inheritTextStyle_ is used to control spans_ in versions below VERSION_EIGHTEEN, preventing them from
108     // adapting to font size automatically.
109     inheritTextStyle_ = textStyle_;
110     MeasureChildren(layoutWrapper, textStyle_);
111     CheckNeedReCreateParagraph(layoutWrapper, textStyle_);
112     ACE_SCOPED_TRACE("TextLayoutAlgorithm::MeasureContent[id:%d][needReCreateParagraph:%d][size:%d]", host->GetId(),
113         needReCreateParagraph_, static_cast<int32_t>(spans_.size()));
114     if (textStyle_.GetTextOverflow() == TextOverflow::MARQUEE) { // create a paragraph with all text in 1 line
115         isMarquee_ = true;
116         auto result = BuildTextRaceParagraph(textStyle_, textLayoutProperty, contentConstraint, layoutWrapper);
117         return result;
118     }
119     if (isSpanStringMode_ && host->LessThanAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN)) {
120         BuildParagraph(textStyle_, textLayoutProperty, contentConstraint, layoutWrapper);
121     } else {
122         if (!AddPropertiesAndAnimations(textStyle_, textLayoutProperty, contentConstraint, layoutWrapper)) {
123             return std::nullopt;
124         }
125     }
126     UpdateRelayoutShaderStyle(layoutWrapper);
127     baselineOffset_ = textStyle_.GetBaselineOffset().ConvertToPxDistribute(
128         textStyle_.GetMinFontScale(), textStyle_.GetMaxFontScale(), textStyle_.IsAllowScale());
129     if (NearZero(contentConstraint.maxSize.Height()) || NearZero(contentConstraint.maxSize.Width()) ||
130         IsParentSizeNearZero(contentConstraint, layoutWrapper)) {
131         return SizeF {};
132     }
133     CHECK_NULL_RETURN(paragraphManager_, std::nullopt);
134 #ifdef ENABLE_ROSEN_BACKEND
135     auto pipeline = host->GetContext();
136     auto fontManager = pipeline == nullptr ? nullptr : pipeline->GetFontManager();
137     if (fontManager != nullptr && Rosen::RSUIDirector::IsHybridRenderEnabled()) {
138         if (static_cast<uint32_t>(paragraphManager_->GetLineCount()) >=
139             Rosen::RSUIDirector::GetHybridRenderTextBlobLenCount()) {
140             fontManager->AddHybridRenderNode(host);
141         } else {
142             fontManager->RemoveHybridRenderNode(host);
143         }
144     }
145 #endif
146     auto height = paragraphManager_->GetHeight();
147     auto maxWidth = paragraphManager_->GetMaxWidth();
148     auto longestLine = paragraphManager_->GetLongestLine();
149     auto heightFinal = static_cast<float>(height + std::fabs(baselineOffset_));
150     if (contentConstraint.selfIdealSize.Height().has_value()) {
151         heightFinal = std::min(heightFinal, contentConstraint.selfIdealSize.Height().value());
152     } else {
153         heightFinal = std::min(heightFinal, contentConstraint.maxSize.Height());
154     }
155     if (host->GetTag() == V2::TEXT_ETS_TAG && textLayoutProperty->GetContent().value_or(u"").empty() &&
156         NonPositive(longestLine)) {
157         ACE_SCOPED_TRACE("TextHeightFinal [%f], TextContentWidth [%f], FontSize [%lf]", heightFinal, maxWidth,
158             textStyle_.GetFontSize().ConvertToPxDistribute(
159                 textStyle_.GetMinFontScale(), textStyle_.GetMaxFontScale(), textStyle_.IsAllowScale()));
160         return SizeF {};
161     }
162     return SizeF(maxWidth, heightFinal);
163 }
164 
UpdateRelayoutShaderStyle(LayoutWrapper * layoutWrapper)165 void TextLayoutAlgorithm::UpdateRelayoutShaderStyle(LayoutWrapper* layoutWrapper)
166 {
167     auto host = layoutWrapper->GetHostNode();
168     CHECK_NULL_VOID(host);
169     auto pattern = host->GetPattern<TextPattern>();
170     CHECK_NULL_VOID(pattern);
171     auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
172     CHECK_NULL_VOID(textLayoutProperty);
173     if (textStyle_.GetGradient().has_value() && !pattern->GetExternalParagraph()) {
174         RelayoutShaderStyle(textLayoutProperty);
175     }
176 }
177 
RelayoutShaderStyle(const RefPtr<TextLayoutProperty> & layoutProperty)178 void TextLayoutAlgorithm::RelayoutShaderStyle(const RefPtr<TextLayoutProperty>& layoutProperty)
179 {
180     CHECK_NULL_VOID(paragraphManager_);
181     auto paragraphs = paragraphManager_->GetParagraphs();
182     if (spans_.empty()) {
183         for (auto pIter = paragraphs.begin(); pIter != paragraphs.end(); pIter++) {
184             auto paragraph = pIter->paragraph;
185             if (!paragraph) {
186                 continue;
187             }
188             auto textStyle = textStyle_;
189             textStyle.SetForeGroundBrushBitMap();
190             paragraph->ReLayoutForeground(textStyle);
191         }
192         return;
193     }
194     if (!spans_.empty()) {
195         size_t itemIndex = -1;
196         for (auto pIter = paragraphs.begin(); pIter != paragraphs.end(); pIter++) {
197             ++itemIndex;
198             auto paragraph = pIter->paragraph;
199             if (!paragraph) {
200                 continue;
201             }
202             if (itemIndex >= spans_.size()) {
203                 return;
204             }
205             auto spans = spans_[itemIndex];
206             TextStyle textStyle;
207             if (!spans.empty() && spans.front() && spans.front()->GetTextStyle() &&
208                 spans.front()->GetTextStyle()->GetGradient().has_value()) {
209                 textStyle = spans.front()->GetTextStyle().value();
210             } else {
211                 textStyle = textStyle_;
212             }
213             textStyle.SetForeGroundBrushBitMap();
214             paragraph->ReLayoutForeground(textStyle);
215         }
216     }
217 }
218 
AddPropertiesAndAnimations(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & textLayoutProperty,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)219 bool TextLayoutAlgorithm::AddPropertiesAndAnimations(TextStyle& textStyle,
220     const RefPtr<TextLayoutProperty>& textLayoutProperty, const LayoutConstraintF& contentConstraint,
221     LayoutWrapper* layoutWrapper)
222 {
223     bool result = false;
224     switch (textLayoutProperty->GetHeightAdaptivePolicyValue(TextHeightAdaptivePolicy::MAX_LINES_FIRST)) {
225         case TextHeightAdaptivePolicy::MAX_LINES_FIRST:
226             result = BuildParagraph(textStyle, textLayoutProperty, contentConstraint, layoutWrapper);
227             break;
228         case TextHeightAdaptivePolicy::MIN_FONT_SIZE_FIRST:
229             result = BuildParagraphAdaptUseMinFontSize(textStyle, textLayoutProperty, contentConstraint, layoutWrapper);
230             break;
231         case TextHeightAdaptivePolicy::LAYOUT_CONSTRAINT_FIRST:
232             result =
233                 BuildParagraphAdaptUseLayoutConstraint(textStyle, textLayoutProperty, contentConstraint, layoutWrapper);
234             break;
235         default:
236             break;
237     }
238     return result;
239 }
240 
CheckNeedReCreateParagraph(LayoutWrapper * layoutWrapper,const TextStyle & textStyle)241 void TextLayoutAlgorithm::CheckNeedReCreateParagraph(LayoutWrapper* layoutWrapper, const TextStyle& textStyle)
242 {
243     auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
244     CHECK_NULL_VOID(textLayoutProperty);
245     auto frameNode = layoutWrapper->GetHostNode();
246     CHECK_NULL_VOID(frameNode);
247     auto textPattern = frameNode->GetPattern<TextPattern>();
248     CHECK_NULL_VOID(textPattern);
249     alwaysReCreateParagraph_ = AlwaysReCreateParagraph(layoutWrapper);
250     needReCreateParagraph_ = textLayoutProperty->GetNeedReCreateParagraphValue(false) ||
251                              textStyle.NeedReCreateParagraph() || alwaysReCreateParagraph_;
252 }
253 
ResetNeedReCreateParagraph(LayoutWrapper * layoutWrapper)254 void TextLayoutAlgorithm::ResetNeedReCreateParagraph(LayoutWrapper* layoutWrapper)
255 {
256     auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
257     CHECK_NULL_VOID(textLayoutProperty);
258     textLayoutProperty->ResetNeedReCreateParagraph();
259     CHECK_NULL_VOID(!alwaysReCreateParagraph_);
260     needReCreateParagraph_ = false;
261 }
262 
AlwaysReCreateParagraph(LayoutWrapper * layoutWrapper)263 bool TextLayoutAlgorithm::AlwaysReCreateParagraph(LayoutWrapper* layoutWrapper)
264 {
265     auto frameNode = layoutWrapper->GetHostNode();
266     CHECK_NULL_RETURN(frameNode, false);
267     auto textPattern = frameNode->GetPattern<TextPattern>();
268     CHECK_NULL_RETURN(textPattern, false);
269     auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
270     CHECK_NULL_RETURN(textLayoutProperty, false);
271     return textPattern->GetExternalParagraph() || textPattern->NeedShowAIDetect() || isSpanStringMode_ ||
272            textPattern->IsDragging() ||
273            textLayoutProperty->GetEllipsisModeValue(EllipsisMode::TAIL) == EllipsisMode::MIDDLE ||
274            textPattern->IsSensitiveEnable() ||
275            textLayoutProperty->GetSymbolTypeValue(SymbolType::SYSTEM) == SymbolType::CUSTOM;
276 }
277 
UpdateParagraphForAISpan(const TextStyle & textStyle,LayoutWrapper * layoutWrapper,const RefPtr<Paragraph> & paragraph)278 void TextLayoutAlgorithm::UpdateParagraphForAISpan(
279     const TextStyle& textStyle, LayoutWrapper* layoutWrapper, const RefPtr<Paragraph>& paragraph)
280 {
281     CHECK_NULL_VOID(layoutWrapper);
282     auto layoutProperty = layoutWrapper->GetLayoutProperty();
283     CHECK_NULL_VOID(layoutProperty);
284     auto frameNode = layoutWrapper->GetHostNode();
285     CHECK_NULL_VOID(frameNode);
286     auto pattern = frameNode->GetPattern<TextPattern>();
287     CHECK_NULL_VOID(pattern);
288     auto wTextForAI = pattern->GetTextForAI();
289     int32_t wTextForAILength = static_cast<int32_t>(wTextForAI.length());
290     int32_t preEnd = 0;
291     DragSpanPosition dragSpanPosition;
292     dragSpanPosition.dragStart = pattern->GetRecoverStart();
293     dragSpanPosition.dragEnd = pattern->GetRecoverEnd();
294     bool isDragging = pattern->IsDragging();
295     TextStyle aiSpanStyle = textStyle;
296     pattern->ModifyAISpanStyle(aiSpanStyle);
297     for (auto kv : pattern->GetAISpanMap()) {
298         if (preEnd >= wTextForAILength) {
299             break;
300         }
301         auto aiSpan = kv.second;
302         if (aiSpan.start < preEnd) {
303             TAG_LOGI(AceLogTag::ACE_TEXT, "Error prediction");
304             continue;
305         }
306         if (preEnd < aiSpan.start) {
307             dragSpanPosition.spanStart = preEnd;
308             dragSpanPosition.spanEnd = aiSpan.start;
309             GrayDisplayAISpan(dragSpanPosition, wTextForAI, textStyle, isDragging, paragraph);
310         }
311         preEnd = aiSpan.end;
312         dragSpanPosition.spanStart = aiSpan.start;
313         dragSpanPosition.spanEnd = aiSpan.end;
314         GrayDisplayAISpan(dragSpanPosition, wTextForAI, aiSpanStyle, isDragging, paragraph);
315     }
316     if (preEnd < wTextForAILength) {
317         dragSpanPosition.spanStart = preEnd;
318         dragSpanPosition.spanEnd = wTextForAILength;
319         GrayDisplayAISpan(dragSpanPosition, wTextForAI, textStyle, isDragging, paragraph);
320     }
321 }
322 
GrayDisplayAISpan(const DragSpanPosition & dragSpanPosition,const std::u16string wTextForAI,const TextStyle & textStyle,bool isDragging,const RefPtr<Paragraph> & paragraph)323 void TextLayoutAlgorithm::GrayDisplayAISpan(const DragSpanPosition& dragSpanPosition, const std::u16string wTextForAI,
324     const TextStyle& textStyle, bool isDragging, const RefPtr<Paragraph>& paragraph)
325 {
326     int32_t dragStart = dragSpanPosition.dragStart;
327     int32_t dragEnd = dragSpanPosition.dragEnd;
328     int32_t spanStart = dragSpanPosition.spanStart;
329     int32_t spanEnd = dragSpanPosition.spanEnd;
330     std::vector<std::u16string> contents = {};
331     std::u16string firstParagraph = u"";
332     std::u16string secondParagraph = u"";
333     std::u16string thirdParagraph = u"";
334     if (dragStart > spanEnd || dragEnd < spanStart || !isDragging) {
335         firstParagraph = StringOutBoundProtection(spanStart, spanEnd - spanStart, wTextForAI);
336     } else if (spanStart <= dragStart && spanEnd >= dragStart && spanEnd <= dragEnd) {
337         firstParagraph = StringOutBoundProtection(spanStart, dragStart - spanStart, wTextForAI);
338         secondParagraph = StringOutBoundProtection(dragStart, spanEnd - dragStart, wTextForAI);
339     } else if (spanStart >= dragStart && spanEnd <= dragEnd) {
340         secondParagraph = StringOutBoundProtection(spanStart, spanEnd - spanStart, wTextForAI);
341     } else if (spanStart <= dragStart && spanEnd >= dragEnd) {
342         firstParagraph = StringOutBoundProtection(spanStart, dragStart - spanStart, wTextForAI);
343         secondParagraph = StringOutBoundProtection(dragStart, dragEnd - dragStart, wTextForAI);
344         thirdParagraph = StringOutBoundProtection(dragEnd, spanEnd - dragEnd, wTextForAI);
345     } else {
346         secondParagraph = StringOutBoundProtection(spanStart, dragEnd - spanStart, wTextForAI);
347         thirdParagraph = StringOutBoundProtection(dragEnd, spanEnd - dragEnd, wTextForAI);
348     }
349     contents = { firstParagraph, secondParagraph, thirdParagraph };
350     CreateParagraphDrag(textStyle, contents, paragraph);
351 }
352 
StringOutBoundProtection(int32_t position,int32_t length,std::u16string wTextForAI)353 std::u16string TextLayoutAlgorithm::StringOutBoundProtection(int32_t position, int32_t length,
354     std::u16string wTextForAI)
355 {
356     int32_t wTextForAILength = static_cast<int32_t>(wTextForAI.length());
357     if (position >= 0 && position < wTextForAILength && length >= 0 && length <= wTextForAILength - position) {
358         return wTextForAI.substr(position, length);
359     }
360     return u"";
361 }
362 
CreateParagraph(const TextStyle & textStyle,std::u16string content,LayoutWrapper * layoutWrapper,double maxWidth)363 bool TextLayoutAlgorithm::CreateParagraph(
364     const TextStyle& textStyle, std::u16string content, LayoutWrapper* layoutWrapper, double maxWidth)
365 {
366     if (!paragraphManager_) {
367         paragraphManager_ = AceType::MakeRefPtr<ParagraphManager>();
368     }
369     auto frameNode = layoutWrapper->GetHostNode();
370     CHECK_NULL_RETURN(frameNode, false);
371     auto pattern = frameNode->GetPattern<TextPattern>();
372     CHECK_NULL_RETURN(pattern, false);
373     pattern->ClearCustomSpanPlaceholderInfo();
374     if (pattern->IsSensitiveEnable()) {
375         UpdateSensitiveContent(content);
376     }
377     auto useExternalParagraph = pattern->GetExternalParagraph() && !pattern->NeedShowAIDetect();
378     auto externalParagraphStyle = pattern->GetExternalParagraphStyle();
379     auto paraStyle = ParagraphUtil::GetParagraphStyle(textStyle);
380     if (pattern->GetExternalParagraph()) {
381         if (!useExternalParagraph && externalParagraphStyle) {
382             paraStyle = externalParagraphStyle.value();
383         }
384     }
385     if (frameNode->GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN) || isSpanStringMode_) {
386         paraStyle.fontSize = textStyle.GetFontSize().ConvertToPxDistribute(
387             textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
388     }
389     paraStyle.leadingMarginAlign = Alignment::CENTER;
390     paraStyle.textStyleUid = frameNode->GetId();
391     // SymbolGlyph
392     if (frameNode->GetTag() == V2::SYMBOL_ETS_TAG) {
393         paragraphManager_->Reset();
394         return UpdateSymbolTextStyle(textStyle, paraStyle, layoutWrapper, frameNode);
395     }
396     if (spans_.empty() || useExternalParagraph) {
397         // only use for text.
398         return UpdateSingleParagraph(layoutWrapper, paraStyle, textStyle, content, maxWidth);
399     } else {
400         paragraphManager_->Reset();
401         return UpdateParagraphBySpan(layoutWrapper, paraStyle, maxWidth, textStyle);
402     }
403 }
404 
UpdateSymbolTextStyle(const TextStyle & textStyle,const ParagraphStyle & paraStyle,LayoutWrapper * layoutWrapper,RefPtr<FrameNode> & frameNode)405 bool TextLayoutAlgorithm::UpdateSymbolTextStyle(const TextStyle& textStyle, const ParagraphStyle& paraStyle,
406     LayoutWrapper* layoutWrapper, RefPtr<FrameNode>& frameNode)
407 {
408     auto&& paragraph = Paragraph::Create(paraStyle, FontCollection::Current());
409     CHECK_NULL_RETURN(paragraph, false);
410     auto layoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
411     CHECK_NULL_RETURN(layoutProperty, false);
412     auto symbolSourceInfo = layoutProperty->GetSymbolSourceInfo();
413     CHECK_NULL_RETURN(symbolSourceInfo, false);
414     auto symbolType = textStyle.GetSymbolType();
415     std::vector<std::string> fontFamilies = textStyle.GetFontFamilies();
416     if (symbolType == SymbolType::CUSTOM && fontFamilies.empty()) {
417         return false;
418     }
419     paragraph->PushStyle(textStyle);
420     if (textStyle.GetSymbolEffectOptions().has_value()) {
421         auto symbolEffectOptions = layoutProperty->GetSymbolEffectOptionsValue(SymbolEffectOptions());
422         symbolEffectOptions.Reset();
423         layoutProperty->UpdateSymbolEffectOptions(symbolEffectOptions);
424     }
425     paragraph->AddSymbol(symbolSourceInfo->GetUnicode());
426     paragraph->PopStyle();
427     paragraph->Build();
428     paragraph->SetParagraphSymbolAnimation(frameNode);
429     paragraphManager_->AddParagraph({ .paragraph = paragraph, .paragraphStyle = paraStyle, .start = 0, .end = 2 });
430     return true;
431 }
432 
CreateParagraphDrag(const TextStyle & textStyle,const std::vector<std::u16string> & contents,const RefPtr<Paragraph> & paragraph)433 void TextLayoutAlgorithm::CreateParagraphDrag(
434     const TextStyle& textStyle, const std::vector<std::u16string>& contents, const RefPtr<Paragraph>& paragraph)
435 {
436     TextStyle dragTextStyle = textStyle;
437     Color color = textStyle.GetTextColor().ChangeAlpha(DRAGGED_TEXT_TRANSPARENCY);
438     dragTextStyle.SetTextColor(color);
439     Color textDecorationColor = textStyle.GetTextDecorationColor().ChangeAlpha(DRAGGED_TEXT_TRANSPARENCY);
440     dragTextStyle.SetTextDecorationColor(textDecorationColor);
441     std::vector<TextStyle> textStyles { textStyle, dragTextStyle, textStyle };
442 
443     CHECK_NULL_VOID(paragraph);
444     for (size_t i = 0; i < contents.size(); i++) {
445         std::u16string splitStr = contents[i];
446         if (splitStr.empty()) {
447             continue;
448         }
449         auto& style = textStyles[i];
450         paragraph->PushStyle(style);
451         StringUtils::TransformStrCase(splitStr, static_cast<int32_t>(style.GetTextCase()));
452         UtfUtils::HandleInvalidUTF16(reinterpret_cast<uint16_t*>(splitStr.data()), splitStr.length(), 0);
453         paragraph->AddText(splitStr);
454         paragraph->PopStyle();
455     }
456 }
457 
ReLayoutParagraphs(const TextStyle & textStyle,LayoutWrapper * layoutWrapper,const SizeF & maxSize)458 bool TextLayoutAlgorithm::ReLayoutParagraphs(
459     const TextStyle& textStyle, LayoutWrapper* layoutWrapper, const SizeF& maxSize)
460 {
461     auto needReLayout = textStyle.NeedReLayout();
462     ParagraphStyle parStyle;
463     std::vector<TextStyle> textStyles;
464     if (!spans_.empty() && !isSpanStringMode_ && !needReCreateParagraph_) {
465         needReLayout |= ReLayoutParagraphBySpan(layoutWrapper, parStyle, textStyle, textStyles);
466         CHECK_NULL_RETURN(!needReCreateParagraph_, false);
467     } else if (!needReCreateParagraph_ && needReLayout) {
468         auto tempTextStyle = textStyle;
469         tempTextStyle.ResetTextBaselineOffset();
470         if (SystemProperties::GetTextTraceEnabled()) {
471             ACE_TEXT_SCOPED_TRACE("TextLayoutAlgorithm::ReLayoutParagraphs[id:%d][textStyleBitmap:%s]",
472                 tempTextStyle.GetTextStyleUid(), tempTextStyle.GetReLayoutTextStyleBitmap().to_string().c_str());
473         }
474         textStyles.emplace_back(tempTextStyle);
475         parStyle = ParagraphUtil::GetParagraphStyle(textStyle);
476         if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
477             parStyle.fontSize = textStyle.GetFontSize().ConvertToPxDistribute(
478                 textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
479         }
480     }
481     auto frameNode = layoutWrapper->GetHostNode();
482     CHECK_NULL_RETURN(frameNode, false);
483     parStyle.textStyleUid = frameNode->GetId();
484     CHECK_NULL_RETURN(paragraphManager_, false);
485     auto paragraphInfo = paragraphManager_->GetParagraphs();
486     for (auto pIter = paragraphInfo.begin(); pIter != paragraphInfo.end(); pIter++) {
487         auto paragraph = pIter->paragraph;
488         CHECK_NULL_RETURN(paragraph, false);
489         if (!needReCreateParagraph_ && needReLayout) {
490             paragraph->ReLayout(maxSize.Width(), parStyle, textStyles);
491         } else {
492             paragraph->Layout(maxSize.Width());
493         }
494     }
495     return true;
496 }
497 
CreateParagraphAndLayout(TextStyle & textStyle,const std::u16string & content,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper,bool needLayout)498 bool TextLayoutAlgorithm::CreateParagraphAndLayout(TextStyle& textStyle, const std::u16string& content,
499     const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper, bool needLayout)
500 {
501     auto maxSize = MultipleParagraphLayoutAlgorithm::GetMaxMeasureSize(contentConstraint);
502     auto needReLayout = textStyle.NeedReLayout();
503     if (SystemProperties::GetTextTraceEnabled()) {
504         ACE_TEXT_SCOPED_TRACE("TextLayoutAlgorithm::CreateParagraphAndLayout[contentConstraint:%s][maxSize:%s][Len:%d]["
505                               "needReCreateParagraph:%d][needReLayout:%d][fontSize:%s][fontColor:%s]",
506             contentConstraint.ToString().c_str(), maxSize.ToString().c_str(), static_cast<int32_t>(content.length()),
507             needReCreateParagraph_, needReLayout, textStyle.GetFontSize().ToString().c_str(),
508             textStyle.GetTextColor().ColorToString().c_str());
509     }
510     if (needReCreateParagraph_ && !CreateParagraph(textStyle, content, layoutWrapper, maxSize.Width())) {
511         return false;
512     }
513 
514     if (needReCreateParagraph_) {
515         CHECK_NULL_RETURN(LayoutParagraphs(maxSize.Width()), false);
516     } else {
517         auto frameNode = layoutWrapper->GetHostNode();
518         CHECK_NULL_RETURN(frameNode, false);
519         auto pattern = frameNode->GetPattern<TextPattern>();
520         CHECK_NULL_RETURN(pattern, false);
521         pattern->RelayoutResetOrUpdateTextEffect();
522         if (!ReLayoutParagraphs(textStyle, layoutWrapper, maxSize)) {
523             CHECK_NULL_RETURN(CreateParagraph(textStyle, content, layoutWrapper, maxSize.Width()), false);
524             CHECK_NULL_RETURN(LayoutParagraphs(maxSize.Width()), false);
525         }
526     }
527     // Reset the flag after each paragraph layout.
528     ResetNeedReCreateParagraph(layoutWrapper);
529     textStyle.ResetReCreateAndReLayoutBitmap();
530     return true;
531 }
532 
LayoutParagraphs(float maxWidth)533 bool TextLayoutAlgorithm::LayoutParagraphs(float maxWidth)
534 {
535     CHECK_NULL_RETURN(paragraphManager_, false);
536     auto paragraphInfo = paragraphManager_->GetParagraphs();
537     for (auto pIter = paragraphInfo.begin(); pIter != paragraphInfo.end(); pIter++) {
538         auto paragraph = pIter->paragraph;
539         CHECK_NULL_RETURN(paragraph, false);
540         paragraph->Layout(maxWidth);
541     }
542     return true;
543 }
544 
GetContentOffset(LayoutWrapper * layoutWrapper)545 OffsetF TextLayoutAlgorithm::GetContentOffset(LayoutWrapper* layoutWrapper)
546 {
547     return SetContentOffset(layoutWrapper);
548 }
549 
AdaptMinTextSize(TextStyle & textStyle,const std::u16string & content,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)550 bool TextLayoutAlgorithm::AdaptMinTextSize(TextStyle& textStyle, const std::u16string& content,
551     const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
552 {
553     ACE_TEXT_SCOPED_TRACE("TextLayoutAlgorithm::AdaptMinTextSize[Length:%d]", static_cast<int32_t>(content.length()));
554     // IsNeedAdaptFontSize
555     double maxFontSize = 0.0;
556     double minFontSize = 0.0;
557     auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
558     CHECK_NULL_RETURN(pipeline, false);
559     GetAdaptMaxMinFontSize(textStyle, maxFontSize, minFontSize, contentConstraint);
560     if (!IsNeedAdaptFontSize(maxFontSize, minFontSize)) {
561         if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) {
562             TAG_LOGW(AceLogTag::ACE_TEXT, "create paragraph fail, contentConstraint:%{public}s",
563                 contentConstraint.ToString().c_str());
564             return false;
565         }
566         return true;
567     }
568     if (NonPositive(contentConstraint.maxSize.Width())) {
569         textStyle.SetFontSize(Dimension(minFontSize));
570         return CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper);
571     }
572     // Get suitableSize and set
573     auto ret = GetSuitableSize(textStyle, content, contentConstraint, layoutWrapper);
574     if (!ret.first) {
575         textStyle.SetFontSize(Dimension(minFontSize));
576         return CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper);
577     } else if (ret.first && NearEqual(textStyle.GetFontSize().Value(), ret.second)) {
578         return true; // The font is already set, no need to call CreateParagraphAndLayout again.
579     } else {
580         textStyle.SetFontSize(Dimension(ret.second));
581         return CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper);
582     }
583 }
584 
585 /**
586  * brief: Find the optimal font size within the range [minFontSize, maxFontSize].
587  * return: std::pair<bool, double>
588  *         - first: A boolean indicating whether a suitable size was found (true if found, false otherwise).
589  *         - second: The optimal font size if found, valid only when first is true.
590  */
GetSuitableSize(TextStyle & textStyle,const std::u16string & content,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)591 std::pair<bool, double> TextLayoutAlgorithm::GetSuitableSize(TextStyle& textStyle, const std::u16string& content,
592     const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
593 {
594     double maxFontSize = 0.0;
595     double minFontSize = 0.0;
596     GetAdaptMaxMinFontSize(textStyle, maxFontSize, minFontSize, contentConstraint);
597     auto step = Dimension(1.0, DimensionUnit::PX);
598 
599     if (GreatNotEqual(textStyle.GetAdaptFontSizeStep().Value(), 0.0)) {
600         step = textStyle.GetAdaptFontSizeStep();
601     }
602     double stepSize = step.ConvertToPxDistribute(textStyle.GetMinFontScale(),
603         textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
604     if (NearEqual(stepSize, 0.0)) {
605         return {false, 0.0};
606     }
607     int32_t stepCount = (maxFontSize - minFontSize) / stepSize;
608 
609     // Compare time complexity: stepCount/2 < log(stepCount)+1, exp2 is fast.
610     if (step.GetAdaptDimensionUnit(step) != DimensionUnit::PX && exp2(stepCount / 2 - 1) < stepCount) {
611         return GetSuitableSizeLD(textStyle, content, contentConstraint, layoutWrapper, stepSize);
612     } else {
613         return GetSuitableSizeBS(textStyle, content, contentConstraint, layoutWrapper, stepSize);
614     }
615 }
616 
GetSuitableSizeLD(TextStyle & textStyle,const std::u16string & content,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper,double stepSize)617 std::pair<bool, double> TextLayoutAlgorithm::GetSuitableSizeLD(TextStyle& textStyle, const std::u16string& content,
618     const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper, double stepSize)
619 {
620     double maxFontSize = 0.0;
621     double minFontSize = 0.0;
622     GetAdaptMaxMinFontSize(textStyle, maxFontSize, minFontSize, contentConstraint);
623     auto maxSize = MultipleParagraphLayoutAlgorithm::GetMaxMeasureSize(contentConstraint);
624 
625     if (NearEqual(stepSize, 0.0)) {
626         return {false, 0.0};
627     }
628     double suitableSize = maxFontSize;
629     uint32_t suitCount = 0;
630     while (GreatOrEqual(suitableSize, minFontSize)) {
631         textStyle.SetFontSize(Dimension(suitableSize));
632         if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) {
633             return {false, 0.0};
634         }
635         if (!DidExceedMaxLines(maxSize)) {
636             return {true, suitableSize};
637         }
638         if (suitCount % HUNDRED == 0) {
639             auto host = layoutWrapper->GetHostNode();
640             CHECK_NULL_RETURN(host, {});
641             TAG_LOGW(AceLogTag::ACE_TEXT,
642                 "suit layout:%{public}d, [id:%{public}d, suitSize:%{public}f, minFontSize:%{public}f, "
643                 "stepSize:%{public}f]",
644                 suitCount, host->GetId(), suitableSize, minFontSize, stepSize);
645         }
646         suitCount++;
647         suitableSize -= stepSize;
648     }
649     return {false, 0.0};
650 }
651 
GetSuitableSizeBS(TextStyle & textStyle,const std::u16string & content,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper,double stepSize)652 std::pair<bool, double> TextLayoutAlgorithm::GetSuitableSizeBS(TextStyle& textStyle, const std::u16string& content,
653     const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper, double stepSize)
654 {
655     double maxFontSize = 0.0;
656     double minFontSize = 0.0;
657     GetAdaptMaxMinFontSize(textStyle, maxFontSize, minFontSize, contentConstraint);
658     auto maxSize = MultipleParagraphLayoutAlgorithm::GetMaxMeasureSize(contentConstraint);
659 
660     // Boundary check: for efficiency and to ensure the optimal size is within [minFontSize, maxFontSize].
661     textStyle.SetFontSize(Dimension(maxFontSize));
662     if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) {
663         TAG_LOGW(AceLogTag::ACE_TEXT, "GetSuitableSizeBS create paragraph fail");
664         return {false, 0.0};
665     }
666     if (!DidExceedMaxLines(maxSize)) {
667         return {true, maxFontSize};
668     }
669 
670     if (NearEqual(stepSize, 0.0)) {
671         return {false, 0.0};
672     }
673     int32_t stepCount = (maxFontSize - minFontSize) / stepSize;
674 
675     // Binary search: to find the optimal size within [minFontSize, maxFontSize].
676     int32_t leftBound = 0;
677     int32_t rightBound = stepCount;
678     int32_t mid = (leftBound + rightBound) / 2;
679     uint32_t suitCount = 0;
680     while (leftBound < rightBound) {
681         double suitSz = minFontSize + mid * stepSize;
682         textStyle.SetFontSize(Dimension(suitSz));
683         if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) {
684             return {false, 0.0};
685         }
686         if (!DidExceedMaxLines(maxSize)) {
687             leftBound = mid;
688         } else {
689             rightBound = mid - 1;
690         }
691         if (suitCount % TWENTY == 0) {
692             auto host = layoutWrapper->GetHostNode();
693             CHECK_NULL_RETURN(host, {});
694             TAG_LOGI(AceLogTag::ACE_TEXT,
695                 "suit layout:%{public}d, [id:%{public}d, suitSz:%{public}f, stepCount:%{public}d, stepSize:%{public}f]",
696                 suitCount, host->GetId(), suitSz, stepCount, stepSize);
697         }
698         suitCount++;
699         mid = (leftBound + rightBound + 1) / 2;
700     }
701     return {true, minFontSize + leftBound * stepSize};
702 }
703 
GetBaselineOffset() const704 float TextLayoutAlgorithm::GetBaselineOffset() const
705 {
706     return baselineOffset_;
707 }
708 
UpdateSingleParagraph(LayoutWrapper * layoutWrapper,ParagraphStyle paraStyle,const TextStyle & textStyle,const std::u16string & content,double maxWidth)709 bool TextLayoutAlgorithm::UpdateSingleParagraph(LayoutWrapper* layoutWrapper, ParagraphStyle paraStyle,
710     const TextStyle& textStyle, const std::u16string& content, double maxWidth)
711 {
712     auto host = layoutWrapper->GetHostNode();
713     CHECK_NULL_RETURN(host, false);
714     ACE_TEXT_SCOPED_TRACE("TextLayoutAlgorithm::UpdateSingleParagraph[id:%d][length:%d][w:%f]", host->GetId(),
715         static_cast<int32_t>(content.length()), maxWidth);
716     auto pattern = host->GetPattern<TextPattern>();
717     CHECK_NULL_RETURN(pattern, false);
718     CHECK_NULL_RETURN(paragraphManager_, false);
719     RefPtr<Paragraph> oldParagraph;
720     auto externalParagraph = pattern->GetExternalParagraph();
721     RefPtr<Paragraph> paragraph;
722     if (externalParagraph) {
723         paragraph = Paragraph::Create(externalParagraph.value());
724     } else {
725         paragraph = Paragraph::Create(paraStyle, FontCollection::Current());
726         auto paragraphs = paragraphManager_->GetParagraphs();
727         if (!paragraphs.empty()) {
728             oldParagraph = paragraphs.front().paragraph;
729         }
730     }
731     paragraphManager_->Reset();
732     CHECK_NULL_RETURN(paragraph, false);
733     auto textStyleTmp = textStyle;
734     textStyleTmp.ResetTextBaselineOffset();
735     paragraph->PushStyle(textStyleTmp);
736     if (pattern->NeedShowAIDetect()) {
737         UpdateParagraphForAISpan(textStyle, layoutWrapper, paragraph);
738     } else {
739         if (pattern->IsDragging()) {
740             auto dragContents = pattern->GetDragContents();
741             CreateParagraphDrag(textStyleTmp, dragContents, paragraph);
742         } else {
743             auto value = content;
744             StringUtils::TransformStrCase(value, static_cast<int32_t>(textStyle.GetTextCase()));
745             UtfUtils::HandleInvalidUTF16(reinterpret_cast<uint16_t*>(value.data()), value.length(), 0);
746             paragraph->AddText(value);
747         }
748     }
749     paragraph->Build();
750     if (paragraph) {
751         CreateOrUpdateTextEffect(oldParagraph, paragraph, pattern, content);
752     }
753     ParagraphUtil::ApplyIndent(paraStyle, paragraph, maxWidth, textStyle, GetIndentMaxWidth(maxWidth));
754     paragraphManager_->AddParagraph({ .paragraph = paragraph,
755         .paragraphStyle = paraStyle,
756         .start = 0,
757         .end = content.length() });
758     return true;
759 }
760 
CreateOrUpdateTextEffect(const RefPtr<Paragraph> & oldParagraph,const RefPtr<Paragraph> & newParagraph,const RefPtr<TextPattern> & textPattern,const std::u16string & content)761 void TextLayoutAlgorithm::CreateOrUpdateTextEffect(const RefPtr<Paragraph>& oldParagraph,
762     const RefPtr<Paragraph>& newParagraph, const RefPtr<TextPattern>& textPattern, const std::u16string& content)
763 {
764     bool needUpdateTypography = false;
765     auto textEffect = textPattern->GetOrCreateTextEffect(content, needUpdateTypography);
766     CHECK_NULL_VOID(textEffect);
767     auto frameNode = textPattern->GetHost();
768     CHECK_NULL_VOID(frameNode);
769     if (SystemProperties::GetTextTraceEnabled()) {
770         ACE_TEXT_SCOPED_TRACE(
771             "TextLayoutAlgorithm::CreateOrUpdateTextEffect[id:%d][needUpdateTypography:%d][content:%s]",
772             frameNode->GetId(), needUpdateTypography, UtfUtils::Str16DebugToStr8(content).c_str());
773     }
774     newParagraph->SetParagraphSymbolAnimation(frameNode);
775     if (needUpdateTypography && oldParagraph) {
776         std::vector<std::pair<RefPtr<Paragraph>, RefPtr<Paragraph>>> paragraphs;
777         auto pair = std::make_pair(oldParagraph, newParagraph);
778         paragraphs.emplace_back(pair);
779         textEffect->UpdateTypography(paragraphs);
780     } else if (!needUpdateTypography) {
781         std::vector<RefPtr<Paragraph>> paragraphs;
782         paragraphs.emplace_back(newParagraph);
783         textEffect->AppendTypography(paragraphs);
784     }
785 }
786 
BuildParagraph(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)787 bool TextLayoutAlgorithm::BuildParagraph(TextStyle& textStyle, const RefPtr<TextLayoutProperty>& layoutProperty,
788     const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
789 {
790     auto host = layoutWrapper->GetHostNode();
791     CHECK_NULL_RETURN(host, false);
792     if (!textStyle.GetAdaptTextSize() ||
793         (!spans_.empty() && host->LessThanAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN))) {
794         if (!CreateParagraphAndLayout(
795             textStyle, layoutProperty->GetContent().value_or(u""), contentConstraint, layoutWrapper)) {
796             TAG_LOGE(AceLogTag::ACE_TEXT, "create paragraph error");
797             return false;
798         }
799     } else {
800         if (!AdaptMinTextSize(
801             textStyle, layoutProperty->GetContent().value_or(u""), contentConstraint, layoutWrapper)) {
802             return false;
803         }
804     }
805     return ParagraphReLayout(contentConstraint);
806 }
807 
BuildParagraphAdaptUseMinFontSize(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)808 bool TextLayoutAlgorithm::BuildParagraphAdaptUseMinFontSize(TextStyle& textStyle,
809     const RefPtr<TextLayoutProperty>& layoutProperty, const LayoutConstraintF& contentConstraint,
810     LayoutWrapper* layoutWrapper)
811 {
812     if (!AdaptMaxTextSize(textStyle, layoutProperty->GetContent().value_or(u""), contentConstraint, layoutWrapper)) {
813         return false;
814     }
815     return ParagraphReLayout(contentConstraint);
816 }
817 
BuildParagraphAdaptUseLayoutConstraint(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)818 bool TextLayoutAlgorithm::BuildParagraphAdaptUseLayoutConstraint(TextStyle& textStyle,
819     const RefPtr<TextLayoutProperty>& layoutProperty, const LayoutConstraintF& contentConstraint,
820     LayoutWrapper* layoutWrapper)
821 {
822     // Create the paragraph and obtain the height.
823     if (!BuildParagraph(textStyle, layoutProperty, contentConstraint, layoutWrapper)) {
824         return false;
825     }
826 
827     CHECK_NULL_RETURN(paragraphManager_, false);
828     if (textStyle.GetMaxLines() == UINT32_MAX) {
829         uint32_t maxLines = GetAdaptedMaxLines(textStyle, contentConstraint);
830         textStyle.SetMaxLines(maxLines);
831     }
832     auto lineCount = static_cast<uint32_t>(paragraphManager_->GetLineCount());
833     lineCount = std::max(std::min(textStyle.GetMaxLines(), lineCount), static_cast<uint32_t>(0));
834     textStyle.SetMaxLines(lineCount);
835     auto disableAdaptTextSize = textStyle.GetAdaptTextSize();
836     if (disableAdaptTextSize) {
837         textStyle.DisableAdaptTextSize();
838     }
839 
840     auto height = static_cast<float>(paragraphManager_->GetHeight());
841     uint32_t adaptCount = 0;
842     while (GreatNotEqual(height, contentConstraint.maxSize.Height())) {
843         auto maxLines = textStyle.GetMaxLines();
844         if (maxLines == 0) {
845             break;
846         } else {
847             maxLines = textStyle.GetMaxLines() - 1;
848             textStyle.SetMaxLines(maxLines);
849         }
850         if (!BuildParagraph(textStyle, layoutProperty, contentConstraint, layoutWrapper)) {
851             if (disableAdaptTextSize) {
852                 textStyle.SetAdaptTextSize(true);
853             }
854             return false;
855         }
856         if (adaptCount % HUNDRED == 0) {
857             auto host = layoutWrapper->GetHostNode();
858             CHECK_NULL_RETURN(host, {});
859             TAG_LOGW(AceLogTag::ACE_TEXT,
860                 "AdaptLayout:%{public}d, [id:%{public}d, height:%{public}f, constraint:%{public}s, "
861                 "maxlines:%{public}d]",
862                 adaptCount, host->GetId(), height, contentConstraint.ToString().c_str(), maxLines);
863         }
864         adaptCount++;
865         height = static_cast<float>(paragraphManager_->GetHeight());
866     }
867     if (disableAdaptTextSize) {
868         textStyle.SetAdaptTextSize(true);
869     }
870     return true;
871 }
872 
BuildTextRaceParagraph(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)873 std::optional<SizeF> TextLayoutAlgorithm::BuildTextRaceParagraph(TextStyle& textStyle,
874     const RefPtr<TextLayoutProperty>& layoutProperty, const LayoutConstraintF& contentConstraint,
875     LayoutWrapper* layoutWrapper)
876 {
877     // create a paragraph with all text in 1 line
878     textStyle.SetTextOverflow(TextOverflow::CLIP);
879     textStyle.SetMaxLines(1);
880     textStyle.SetTextIndent(Dimension(0.0f));
881     std::u16string content = layoutProperty->GetContent().value_or(u"");
882     std::replace(content.begin(), content.end(), u'\n', u' ');
883     if (!textStyle.GetAdaptTextSize()) {
884         if (!CreateParagraph(textStyle, content, layoutWrapper)) {
885             return std::nullopt;
886         }
887         textStyle.ResetReCreateAndReLayoutBitmap();
888     } else {
889         if (!AdaptMinTextSize(textStyle, content, contentConstraint, layoutWrapper)) {
890             return std::nullopt;
891         }
892     }
893     layoutProperty->OnPropertyChangeMeasure();
894 
895     textStyle_ = textStyle;
896     auto paragraph = GetSingleParagraph();
897     // layout the paragraph to the width of text
898     paragraph->Layout(std::numeric_limits<float>::max());
899     float paragraphWidth = paragraph->GetLongestLineWithIndent();
900     if (contentConstraint.selfIdealSize.Width().has_value()) {
901         paragraphWidth = std::max(contentConstraint.selfIdealSize.Width().value(), paragraphWidth);
902     } else {
903         paragraphWidth = std::max(contentConstraint.minSize.Width(), paragraphWidth);
904     }
905     paragraphWidth = std::ceil(paragraphWidth);
906     paragraph->Layout(paragraphWidth);
907     UpdateRelayoutShaderStyle(layoutWrapper);
908 
909     auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
910     // calculate the content size
911     auto height = static_cast<float>(paragraph->GetHeight());
912     baselineOffset_ = static_cast<float>(
913         layoutProperty->GetBaselineOffsetValue(Dimension())
914             .ConvertToPxDistribute(textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale()));
915     float heightFinal =
916         std::min(static_cast<float>(height + std::fabs(baselineOffset_)), contentConstraint.maxSize.Height());
917 
918     float widthFinal = paragraphWidth;
919     if (contentConstraint.selfIdealSize.Width().has_value()) {
920         if (contentConstraint.selfIdealSize.Width().value() < paragraphWidth) {
921             widthFinal = contentConstraint.selfIdealSize.Width().value();
922         }
923     } else if (contentConstraint.maxSize.Width() < paragraphWidth) {
924         widthFinal = contentConstraint.maxSize.Width();
925     }
926     return SizeF(widthFinal, heightFinal);
927 }
928 
AdaptMaxTextSize(TextStyle & textStyle,const std::u16string & content,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)929 bool TextLayoutAlgorithm::AdaptMaxTextSize(TextStyle& textStyle, const std::u16string& content,
930     const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
931 {
932     constexpr Dimension ADAPT_UNIT = 1.0_fp;
933     auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
934     CHECK_NULL_RETURN(textLayoutProperty, false);
935     auto step = textLayoutProperty->GetAdaptFontSizeStepValue(ADAPT_UNIT);
936     return AdaptMaxFontSize(textStyle, content, step, contentConstraint, layoutWrapper);
937 }
938 
UpdateSensitiveContent(std::u16string & content)939 void TextLayoutAlgorithm::UpdateSensitiveContent(std::u16string& content)
940 {
941     std::replace_if(
942         content.begin(), content.end(),
943         [](char16_t ch) {
944             return ch != u'\n';
945         }, u'-');
946 }
947 
GetTextStyle() const948 const TextStyle& TextLayoutAlgorithm::GetTextStyle() const
949 {
950     return textStyle_;
951 }
952 
GetLineCount() const953 size_t TextLayoutAlgorithm::GetLineCount() const
954 {
955     size_t count = 0;
956     CHECK_NULL_RETURN(paragraphManager_, 0);
957     auto paragraphInfo = paragraphManager_->GetParagraphs();
958     for (auto pIter = paragraphInfo.begin(); pIter != paragraphInfo.end(); pIter++) {
959         auto paragraph = pIter->paragraph;
960         CHECK_NULL_RETURN(paragraph, 0);
961         count += paragraph->GetLineCount();
962     }
963     return count;
964 }
965 
DidExceedMaxLines(const SizeF & maxSize)966 bool TextLayoutAlgorithm::DidExceedMaxLines(const SizeF& maxSize)
967 {
968     CHECK_NULL_RETURN(paragraphManager_, false);
969     bool didExceedMaxLines = paragraphManager_->DidExceedMaxLinesInner();
970     didExceedMaxLines = didExceedMaxLines || GreatNotEqual(paragraphManager_->GetHeight(), maxSize.Height());
971     didExceedMaxLines =
972         didExceedMaxLines || GreatNotEqual(paragraphManager_->GetLongestLineWithIndent(), maxSize.Width());
973     return didExceedMaxLines;
974 }
975 
IsAdaptExceedLimit(const SizeF & maxSize)976 bool TextLayoutAlgorithm::IsAdaptExceedLimit(const SizeF& maxSize)
977 {
978     CHECK_NULL_RETURN(paragraphManager_, false);
979     return (paragraphManager_->GetLineCount() > 1) || paragraphManager_->DidExceedMaxLinesInner() ||
980            GreatNotEqual(paragraphManager_->GetLongestLineWithIndent(), maxSize.Width());
981 }
982 
IsParentSizeNearZero(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)983 bool TextLayoutAlgorithm::IsParentSizeNearZero(const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
984 {
985     auto widthPolicy = TextBase::GetLayoutCalPolicy(layoutWrapper, true);
986     if (widthPolicy == LayoutCalPolicy::MATCH_PARENT &&
987         (contentConstraint.parentIdealSize.Width().has_value() &&
988             NearZero(contentConstraint.parentIdealSize.Width().value()))) {
989         return true;
990     }
991     auto heightPolicy = TextBase::GetLayoutCalPolicy(layoutWrapper, false);
992     if (heightPolicy == LayoutCalPolicy::MATCH_PARENT &&
993         (contentConstraint.parentIdealSize.Height().has_value() &&
994             NearZero(contentConstraint.parentIdealSize.Height().value()))) {
995         return true;
996     }
997     return false;
998 }
999 
IsFixIdealSizeAndNoMaxSize(LayoutWrapper * layoutWrapper,bool isWidth)1000 bool TextLayoutAlgorithm::IsFixIdealSizeAndNoMaxSize(LayoutWrapper* layoutWrapper, bool isWidth)
1001 {
1002     CHECK_NULL_RETURN(layoutWrapper, false);
1003     auto layoutPolicy = TextBase::GetLayoutCalPolicy(layoutWrapper, isWidth);
1004     if (layoutPolicy == LayoutCalPolicy::FIX_AT_IDEAL_SIZE) {
1005         const auto& layoutProperty = layoutWrapper->GetLayoutProperty();
1006         CHECK_NULL_RETURN(layoutProperty, false);
1007         const auto& calcLayoutConstraint = layoutProperty->GetCalcLayoutConstraint();
1008         CHECK_NULL_RETURN(calcLayoutConstraint, false);
1009         return isWidth ? !calcLayoutConstraint->maxSize->Width().has_value()
1010                        : !calcLayoutConstraint->maxSize->Height().has_value();
1011     }
1012     return false;
1013 }
1014 
CalcContentConstraint(const LayoutConstraintF & constraint,LayoutWrapper * layoutWrapper)1015 LayoutConstraintF TextLayoutAlgorithm::CalcContentConstraint(
1016     const LayoutConstraintF& constraint, LayoutWrapper* layoutWrapper)
1017 {
1018     LayoutConstraintF contentConstraint = constraint;
1019     CHECK_NULL_RETURN(layoutWrapper, contentConstraint);
1020     auto widthPolicy = TextBase::GetLayoutCalPolicy(layoutWrapper, true);
1021     isFixIdealSizeAndNoMaxWidth_ = IsFixIdealSizeAndNoMaxSize(layoutWrapper, true);
1022     if (widthPolicy == LayoutCalPolicy::FIX_AT_IDEAL_SIZE) {
1023         auto maxCalcWidth = GetCalcLayoutConstraintLength(layoutWrapper, true, true);
1024         if (maxCalcWidth) {
1025             contentConstraint.maxSize.SetWidth(maxCalcWidth.value());
1026         } else {
1027             contentConstraint.maxSize.SetWidth(std::numeric_limits<double>::infinity());
1028         }
1029     } else if (widthPolicy == LayoutCalPolicy::MATCH_PARENT && constraint.parentIdealSize.Width().has_value()) {
1030         contentConstraint.selfIdealSize.SetWidth(constraint.parentIdealSize.Width().value());
1031     }
1032     auto heightPolicy = TextBase::GetLayoutCalPolicy(layoutWrapper, false);
1033     if (heightPolicy == LayoutCalPolicy::FIX_AT_IDEAL_SIZE) {
1034         auto maxCalcHeight = GetCalcLayoutConstraintLength(layoutWrapper, true, false);
1035         if (maxCalcHeight) {
1036             contentConstraint.maxSize.SetHeight(maxCalcHeight.value());
1037         } else {
1038             contentConstraint.maxSize.SetHeight(std::numeric_limits<double>::infinity());
1039         }
1040     } else if (heightPolicy == LayoutCalPolicy::MATCH_PARENT && constraint.parentIdealSize.Height().has_value()) {
1041         contentConstraint.selfIdealSize.SetHeight(constraint.parentIdealSize.Height().value());
1042     }
1043     cachedCalcContentConstraint_ = contentConstraint;
1044     return contentConstraint;
1045 }
1046 
IsNeedParagraphReLayout() const1047 bool TextLayoutAlgorithm::IsNeedParagraphReLayout() const
1048 {
1049     return isFixIdealSizeAndNoMaxWidth_;
1050 }
1051 
GetIndentMaxWidth(double width) const1052 double TextLayoutAlgorithm::GetIndentMaxWidth(double width) const
1053 {
1054     if (isFixIdealSizeAndNoMaxWidth_ && cachedCalcContentConstraint_.has_value()) {
1055         return cachedCalcContentConstraint_.value().maxSize.Width();
1056     }
1057     return width;
1058 }
1059 
GetCalcLayoutConstraintLength(LayoutWrapper * layoutWrapper,bool isMax,bool isWidth)1060 std::optional<float> TextLayoutAlgorithm::GetCalcLayoutConstraintLength(
1061     LayoutWrapper* layoutWrapper, bool isMax, bool isWidth)
1062 {
1063     auto layoutProperty = layoutWrapper->GetLayoutProperty();
1064     CHECK_NULL_RETURN(layoutProperty, std::nullopt);
1065     const auto& layoutCalcConstraint = layoutProperty->GetCalcLayoutConstraint();
1066     CHECK_NULL_RETURN(layoutCalcConstraint, std::nullopt);
1067     auto layoutConstraint = layoutProperty->GetLayoutConstraint();
1068     CHECK_NULL_RETURN(layoutConstraint, std::nullopt);
1069     auto calcLayoutConstraintMaxMinSize = isMax ? layoutCalcConstraint->maxSize : layoutCalcConstraint->minSize;
1070     CHECK_NULL_RETURN(calcLayoutConstraintMaxMinSize, std::nullopt);
1071     auto optionalCalcLength =
1072         isWidth ? calcLayoutConstraintMaxMinSize->Width() : calcLayoutConstraintMaxMinSize->Height();
1073     auto percentLength =
1074         isWidth ? layoutConstraint->percentReference.Width() : layoutConstraint->percentReference.Height();
1075     CHECK_NULL_RETURN(optionalCalcLength, std::nullopt);
1076     return ConvertToPx(optionalCalcLength, ScaleProperty::CreateScaleProperty(), percentLength);
1077 }
1078 
MeasureWithFixAtIdealSize(LayoutWrapper * layoutWrapper)1079 void TextLayoutAlgorithm::MeasureWithFixAtIdealSize(LayoutWrapper* layoutWrapper)
1080 {
1081     CHECK_NULL_VOID(layoutWrapper);
1082     auto widthPolicy = TextBase::GetLayoutCalPolicy(layoutWrapper, true);
1083     auto heightPolicy = TextBase::GetLayoutCalPolicy(layoutWrapper, false);
1084     if (widthPolicy != LayoutCalPolicy::FIX_AT_IDEAL_SIZE && heightPolicy != LayoutCalPolicy::FIX_AT_IDEAL_SIZE) {
1085         return;
1086     }
1087     auto geometryNode = layoutWrapper->GetGeometryNode();
1088     CHECK_NULL_VOID(geometryNode);
1089     auto& content = geometryNode->GetContent();
1090     CHECK_NULL_VOID(content);
1091     auto layoutProperty = layoutWrapper->GetLayoutProperty();
1092     CHECK_NULL_VOID(layoutProperty);
1093     auto padding = layoutProperty->CreatePaddingAndBorder();
1094     auto contentSize = content->GetRect().GetSize();
1095     AddPaddingToSize(padding, contentSize);
1096     OptionalSizeF frameSize;
1097     frameSize.UpdateIllegalSizeWithCheck(contentSize);
1098     frameSize = UpdateOptionSizeByCalcLayoutConstraint(
1099         frameSize, layoutProperty->GetCalcLayoutConstraint(), layoutProperty->GetLayoutConstraint()->percentReference);
1100     auto fixSize = frameSize.ConvertToSizeT();
1101     auto measureSize = geometryNode->GetFrameSize();
1102     if (widthPolicy == LayoutCalPolicy::FIX_AT_IDEAL_SIZE) {
1103         measureSize.SetWidth(fixSize.Width());
1104     }
1105     if (heightPolicy == LayoutCalPolicy::FIX_AT_IDEAL_SIZE) {
1106         measureSize.SetHeight(fixSize.Height());
1107     }
1108     geometryNode->SetFrameSize(measureSize);
1109 }
1110 
MeasureWidthLayoutCalPolicy(LayoutWrapper * layoutWrapper)1111 void TextLayoutAlgorithm::MeasureWidthLayoutCalPolicy(LayoutWrapper* layoutWrapper)
1112 {
1113     MeasureWithFixAtIdealSize(layoutWrapper);
1114 }
1115 } // namespace OHOS::Ace::NG
1116