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