• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 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/multiple_paragraph_layout_algorithm.h"
17 
18 #include "text_layout_adapter.h"
19 
20 #include "base/geometry/dimension.h"
21 #include "base/log/ace_performance_monitor.h"
22 #include "base/i18n/localization.h"
23 #include "base/utils/utils.h"
24 #include "core/common/font_manager.h"
25 #include "core/components/common/properties/alignment.h"
26 #include "core/components/common/properties/text_style.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/render/font_collection.h"
32 #include "core/components_ng/render/paragraph.h"
33 #include "core/pipeline_ng/pipeline_context.h"
34 #include "frameworks/bridge/common/utils/utils.h"
35 
36 namespace OHOS::Ace::NG {
37 namespace {
38 constexpr int32_t SYMBOL_SPAN_LENGTH = 2;
GetContentOffsetY(LayoutWrapper * layoutWrapper)39 float GetContentOffsetY(LayoutWrapper* layoutWrapper)
40 {
41     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
42     const auto& padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
43     auto offsetY = padding.top.value_or(0);
44     auto align = Alignment::CENTER;
45     if (layoutWrapper->GetLayoutProperty()->GetPositionProperty()) {
46         align = layoutWrapper->GetLayoutProperty()->GetPositionProperty()->GetAlignment().value_or(align);
47     }
48     const auto& content = layoutWrapper->GetGeometryNode()->GetContent();
49     if (content) {
50         offsetY += Alignment::GetAlignPosition(size, content->GetRect().GetSize(), align).GetY();
51     }
52     return offsetY;
53 }
54 } // namespace
55 
ConstructTextStyles(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper,TextStyle & textStyle)56 void MultipleParagraphLayoutAlgorithm::ConstructTextStyles(
57     const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper, TextStyle& textStyle)
58 {
59     if (Negative(contentConstraint.maxSize.Width()) || Negative(contentConstraint.maxSize.Height())) {
60         return;
61     }
62 
63     auto frameNode = layoutWrapper->GetHostNode();
64     CHECK_NULL_VOID(frameNode);
65     auto pipeline = frameNode->GetContextRefPtr();
66     CHECK_NULL_VOID(pipeline);
67     auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
68     CHECK_NULL_VOID(textLayoutProperty);
69     auto pattern = frameNode->GetPattern<TextPattern>();
70     CHECK_NULL_VOID(pattern);
71     auto contentModifier = pattern->GetContentModifier();
72 
73     textStyle = CreateTextStyleUsingTheme(
74         textLayoutProperty->GetFontStyle(), textLayoutProperty->GetTextLineStyle(), pipeline->GetTheme<TextTheme>());
75     auto fontManager = pipeline->GetFontManager();
76     if (fontManager && !(fontManager->GetAppCustomFont().empty()) &&
77         !(textLayoutProperty->GetFontFamily().has_value())) {
78         textStyle.SetFontFamilies(Framework::ConvertStrToFontFamilies(fontManager->GetAppCustomFont()));
79     }
80     if (contentModifier) {
81         SetPropertyToModifier(textLayoutProperty, contentModifier, textStyle);
82         if (!textLayoutProperty->GetIsAnimationNeeded().has_value() ||
83             textLayoutProperty->GetIsAnimationNeeded().value()) {
84             contentModifier->ModifyTextStyle(textStyle);
85         }
86         contentModifier->SetFontReady(false);
87     }
88     textStyle.SetHalfLeading(textLayoutProperty->GetHalfLeadingValue(pipeline->GetHalfLeading()));
89     // Register callback for fonts.
90     FontRegisterCallback(frameNode, textStyle);
91 
92     // Determines whether a foreground color is set or inherited.
93     UpdateTextColorIfForeground(frameNode, textStyle);
94 }
95 
Measure(LayoutWrapper * layoutWrapper)96 void MultipleParagraphLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
97 {
98     // child constraint has already been calculated by the UpdateParagraphBySpan method when triggering MeasureContent
99     BoxLayoutAlgorithm::PerformMeasureSelf(layoutWrapper);
100     auto baselineDistance = 0.0f;
101     auto paragraph = GetSingleParagraph();
102     if (paragraph) {
103         baselineDistance = paragraph->GetAlphabeticBaseline() + std::max(GetBaselineOffset(), 0.0f);
104     }
105     if (!NearZero(baselineDistance, 0.0f)) {
106         baselineDistance += GetContentOffsetY(layoutWrapper);
107     }
108     layoutWrapper->GetGeometryNode()->SetBaselineDistance(baselineDistance);
109 }
110 
Layout(LayoutWrapper * layoutWrapper)111 void MultipleParagraphLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
112 {
113     CHECK_NULL_VOID(layoutWrapper);
114     auto contentOffset = GetContentOffset(layoutWrapper);
115     CHECK_NULL_VOID(paragraphManager_);
116     auto frameNode = layoutWrapper->GetHostNode();
117     CHECK_NULL_VOID(frameNode);
118     auto pattern = frameNode->GetPattern<TextPattern>();
119     CHECK_NULL_VOID(pattern);
120     std::vector<int32_t> placeholderIndex;
121     GetChildrenPlaceholderIndex(placeholderIndex);
122     auto rectsForPlaceholders = paragraphManager_->GetPlaceholderRects();
123     if (spans_.empty() || placeholderIndex.empty()) {
124         pattern->InitSpanImageLayout(placeholderIndex, rectsForPlaceholders, contentOffset);
125         return;
126     }
127     size_t index = 0;
128     const auto& children = layoutWrapper->GetAllChildrenWithBuild();
129     // children only contains the image span.
130     for (const auto& child : children) {
131         if (!child) {
132             ++index;
133             continue;
134         }
135         if (index >= placeholderIndex.size() || index < 0) {
136             child->SetActive(false);
137             continue;
138         }
139         auto indexTemp = placeholderIndex.at(index);
140         if (indexTemp >= static_cast<int32_t>(rectsForPlaceholders.size()) || indexTemp < 0) {
141             child->SetActive(false);
142             continue;
143         }
144         child->SetActive(true);
145         auto rect = rectsForPlaceholders.at(indexTemp) - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
146         auto geometryNode = child->GetGeometryNode();
147         if (!geometryNode) {
148             ++index;
149             continue;
150         }
151         geometryNode->SetMarginFrameOffset(contentOffset + OffsetF(rect.Left(), rect.Top()));
152         child->Layout();
153         ++index;
154     }
155     pattern->InitSpanImageLayout(placeholderIndex, rectsForPlaceholders, contentOffset);
156 }
157 
GetChildrenPlaceholderIndex(std::vector<int32_t> & placeholderIndex)158 void MultipleParagraphLayoutAlgorithm::GetChildrenPlaceholderIndex(std::vector<int32_t>& placeholderIndex)
159 {
160     for (auto&& group : spans_) {
161         for (const auto& child : group) {
162             if (!child) {
163                 continue;
164             }
165             if (AceType::InstanceOf<CustomSpanItem>(child)) {
166                 continue;
167             }
168             if (AceType::InstanceOf<ImageSpanItem>(child) || AceType::InstanceOf<PlaceholderSpanItem>(child)) {
169                 placeholderIndex.emplace_back(child->placeholderIndex);
170             }
171         }
172     }
173 }
174 
GetSpanParagraphStyle(LayoutWrapper * layoutWrapper,const RefPtr<SpanItem> & spanItem,ParagraphStyle & pStyle)175 void MultipleParagraphLayoutAlgorithm::GetSpanParagraphStyle(
176     LayoutWrapper* layoutWrapper, const RefPtr<SpanItem>& spanItem, ParagraphStyle& pStyle)
177 {
178     const auto& lineStyle = spanItem->textLineStyle;
179     CHECK_NULL_VOID(lineStyle);
180     if (lineStyle->HasTextAlign()) {
181         pStyle.align = lineStyle->GetTextAlignValue();
182     }
183     if (lineStyle->HasMaxLines()) {
184         pStyle.maxLines = lineStyle->GetMaxLinesValue();
185     }
186     if (lineStyle->HasWordBreak()) {
187         pStyle.wordBreak = lineStyle->GetWordBreakValue();
188     }
189     if (lineStyle->HasEllipsisMode()) {
190         pStyle.ellipsisMode = lineStyle->GetEllipsisModeValue();
191     }
192     if (lineStyle->HasTextOverflow()) {
193         pStyle.textOverflow = lineStyle->GetTextOverflowValue();
194     }
195     if (lineStyle->HasTextIndent()) {
196         pStyle.indent = lineStyle->GetTextIndentValue();
197     }
198     if (lineStyle->HasLineBreakStrategy()) {
199         pStyle.lineBreakStrategy = lineStyle->GetLineBreakStrategyValue();
200     }
201     if (lineStyle->HasLeadingMargin()) {
202         pStyle.leadingMargin = lineStyle->GetLeadingMarginValue();
203     }
204     if (lineStyle->HasLineHeight()) {
205         pStyle.lineHeight = lineStyle->GetLineHeightValue();
206     }
207     if (layoutWrapper) {
208         pStyle.direction = GetTextDirection(spanItem->content, layoutWrapper);
209     } else {
210         pStyle.direction = GetTextDirectionByContent(spanItem->content);
211     }
212 }
213 
FontRegisterCallback(const RefPtr<FrameNode> & frameNode,const TextStyle & textStyle)214 void MultipleParagraphLayoutAlgorithm::FontRegisterCallback(
215     const RefPtr<FrameNode>& frameNode, const TextStyle& textStyle)
216 {
217     auto callback = [weakNode = WeakPtr<FrameNode>(frameNode)] {
218         auto frameNode = weakNode.Upgrade();
219         CHECK_NULL_VOID(frameNode);
220         frameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
221         auto pattern = frameNode->GetPattern<TextPattern>();
222         CHECK_NULL_VOID(pattern);
223         auto modifier = DynamicCast<TextContentModifier>(pattern->GetContentModifier());
224         CHECK_NULL_VOID(modifier);
225         modifier->SetFontReady(true);
226     };
227     auto pipeline = frameNode->GetContext();
228     CHECK_NULL_VOID(pipeline);
229     auto fontManager = pipeline->GetFontManager();
230     if (fontManager) {
231         bool isCustomFont = false;
232         for (const auto& familyName : textStyle.GetFontFamilies()) {
233             bool customFont = fontManager->RegisterCallbackNG(frameNode, familyName, callback);
234             if (customFont) {
235                 isCustomFont = true;
236             }
237         }
238         if (isCustomFont || fontManager->IsDefaultFontChanged()) {
239             auto pattern = frameNode->GetPattern<TextPattern>();
240             CHECK_NULL_VOID(pattern);
241             pattern->SetIsCustomFont(true);
242             auto modifier = DynamicCast<TextContentModifier>(pattern->GetContentModifier());
243             CHECK_NULL_VOID(modifier);
244             modifier->SetIsCustomFont(true);
245         }
246     }
247 }
248 
UpdateTextColorIfForeground(const RefPtr<FrameNode> & frameNode,TextStyle & textStyle)249 void MultipleParagraphLayoutAlgorithm::UpdateTextColorIfForeground(
250     const RefPtr<FrameNode>& frameNode, TextStyle& textStyle)
251 {
252     auto renderContext = frameNode->GetRenderContext();
253     if (renderContext->HasForegroundColor()) {
254         if (renderContext->GetForegroundColorValue().GetValue() != textStyle.GetTextColor().GetValue()) {
255             textStyle.SetTextColor(Color::FOREGROUND);
256         }
257     } else if (renderContext->HasForegroundColorStrategy()) {
258         textStyle.SetTextColor(Color::FOREGROUND);
259     }
260 }
261 
SetFontSizePropertyToModifier(const RefPtr<TextLayoutProperty> & layoutProperty,const RefPtr<TextContentModifier> & modifier,const TextStyle & textStyle)262 void MultipleParagraphLayoutAlgorithm::SetFontSizePropertyToModifier(const RefPtr<TextLayoutProperty>& layoutProperty,
263     const RefPtr<TextContentModifier>& modifier, const TextStyle& textStyle)
264 {
265     auto fontSize = layoutProperty->GetFontSize();
266     if (fontSize.has_value()) {
267         modifier->SetFontSize(fontSize.value(), textStyle);
268     } else {
269         // Reset modifier FontSize.
270         modifier->SetFontSize(textStyle.GetFontSize(), textStyle, true);
271     }
272     auto adaptMinFontSize = layoutProperty->GetAdaptMinFontSize();
273     if (adaptMinFontSize.has_value()) {
274         modifier->SetAdaptMinFontSize(adaptMinFontSize.value(), textStyle);
275     } else {
276         // Reset modifier MinFontSize.
277         modifier->SetAdaptMinFontSize(textStyle.GetAdaptMinFontSize(), textStyle, true);
278     }
279     auto adaptMaxFontSize = layoutProperty->GetAdaptMaxFontSize();
280     if (adaptMaxFontSize.has_value()) {
281         modifier->SetAdaptMaxFontSize(adaptMaxFontSize.value(), textStyle);
282     } else {
283         // Reset modifier MaxFontSize.
284         modifier->SetAdaptMaxFontSize(textStyle.GetAdaptMaxFontSize(), textStyle, true);
285     }
286 }
287 
SetDecorationPropertyToModifier(const RefPtr<TextLayoutProperty> & layoutProperty,const RefPtr<TextContentModifier> & modifier,const TextStyle & textStyle)288 void MultipleParagraphLayoutAlgorithm::SetDecorationPropertyToModifier(const RefPtr<TextLayoutProperty>& layoutProperty,
289     const RefPtr<TextContentModifier>& modifier, const TextStyle& textStyle)
290 {
291     auto textDecorationColor = layoutProperty->GetTextDecorationColor();
292     if (textDecorationColor.has_value()) {
293         modifier->SetTextDecorationColor(textDecorationColor.value());
294     } else {
295         modifier->SetTextDecorationColor(textStyle.GetTextDecorationColor(), true);
296     }
297     auto textDecoration = layoutProperty->GetTextDecoration();
298     if (textDecoration.has_value()) {
299         modifier->SetTextDecoration(textDecoration.value());
300     } else {
301         modifier->SetTextDecoration(textStyle.GetTextDecoration(), true);
302     }
303 }
304 
SetPropertyToModifier(const RefPtr<TextLayoutProperty> & layoutProperty,const RefPtr<TextContentModifier> & modifier,const TextStyle & textStyle)305 void MultipleParagraphLayoutAlgorithm::SetPropertyToModifier(const RefPtr<TextLayoutProperty>& layoutProperty,
306     const RefPtr<TextContentModifier>& modifier, const TextStyle& textStyle)
307 {
308     SetFontSizePropertyToModifier(layoutProperty, modifier, textStyle);
309     auto fontWeight = layoutProperty->GetFontWeight();
310     if (fontWeight.has_value()) {
311         modifier->SetFontWeight(fontWeight.value());
312     } else {
313         modifier->SetFontWeight(textStyle.GetFontWeight(), true);
314     }
315     auto textColor = layoutProperty->GetTextColor();
316     if (textColor.has_value()) {
317         modifier->SetTextColor(textColor.value());
318     } else {
319         modifier->SetTextColor(textStyle.GetTextColor(), true);
320     }
321     auto textShadow = layoutProperty->GetTextShadow();
322     if (textShadow.has_value()) {
323         modifier->SetTextShadow(textShadow.value());
324     } else {
325         modifier->SetTextShadow(textStyle.GetTextShadows());
326     }
327     SetDecorationPropertyToModifier(layoutProperty, modifier, textStyle);
328     auto baselineOffset = layoutProperty->GetBaselineOffset();
329     if (baselineOffset.has_value()) {
330         modifier->SetBaselineOffset(baselineOffset.value(), textStyle);
331     } else {
332         modifier->SetBaselineOffset(textStyle.GetBaselineOffset(), textStyle, true);
333     }
334     auto lineHeight = layoutProperty->GetLineHeight();
335     if (lineHeight.has_value()) {
336         if (lineHeight->Unit() == DimensionUnit::PERCENT) {
337             modifier->SetLineHeight(lineHeight.value(), textStyle, true);
338         } else {
339             modifier->SetLineHeight(lineHeight.value(), textStyle);
340         }
341     } else {
342         modifier->SetLineHeight(textStyle.GetLineHeight(), textStyle, true);
343     }
344 }
345 
GetSingleParagraph() const346 RefPtr<Paragraph> MultipleParagraphLayoutAlgorithm::GetSingleParagraph() const
347 {
348     CHECK_NULL_RETURN(paragraphManager_, nullptr);
349     CHECK_NULL_RETURN(!paragraphManager_->GetParagraphs().empty(), nullptr);
350     auto paragraphInfo = paragraphManager_->GetParagraphs().front();
351     auto paragraph = paragraphInfo.paragraph;
352     CHECK_NULL_RETURN(paragraph, nullptr);
353     return paragraph;
354 }
355 
SetContentOffset(LayoutWrapper * layoutWrapper)356 OffsetF MultipleParagraphLayoutAlgorithm::SetContentOffset(LayoutWrapper* layoutWrapper)
357 {
358     OffsetF contentOffset(0.0f, 0.0f);
359     CHECK_NULL_RETURN(layoutWrapper, contentOffset);
360 
361     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
362     const auto& padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
363     MinusPaddingToSize(padding, size);
364     auto left = padding.left.value_or(0);
365     auto top = padding.top.value_or(0);
366     auto paddingOffset = OffsetF(left, top);
367     auto align = Alignment::CENTER;
368     if (layoutWrapper->GetLayoutProperty()->GetPositionProperty()) {
369         align = layoutWrapper->GetLayoutProperty()->GetPositionProperty()->GetAlignment().value_or(align);
370     }
371 
372     const auto& content = layoutWrapper->GetGeometryNode()->GetContent();
373     if (content) {
374         contentOffset = Alignment::GetAlignPosition(size, content->GetRect().GetSize(), align) + paddingOffset;
375         content->SetOffset(contentOffset);
376     }
377     return contentOffset;
378 }
379 
GetParagraphStyle(const TextStyle & textStyle,const std::string & content,LayoutWrapper * layoutWrapper) const380 ParagraphStyle MultipleParagraphLayoutAlgorithm::GetParagraphStyle(
381     const TextStyle& textStyle, const std::string& content, LayoutWrapper* layoutWrapper) const
382 {
383     return { .direction = GetTextDirection(content, layoutWrapper),
384         .align = textStyle.GetTextAlign(),
385         .maxLines = static_cast<int32_t>(textStyle.GetMaxLines()) < 0 ? UINT32_MAX : textStyle.GetMaxLines(),
386         .fontLocale = Localization::GetInstance()->GetFontLocale(),
387         .wordBreak = textStyle.GetWordBreak(),
388         .ellipsisMode = textStyle.GetEllipsisMode(),
389         .lineBreakStrategy = textStyle.GetLineBreakStrategy(),
390         .textOverflow = textStyle.GetTextOverflow(),
391         .indent = textStyle.GetTextIndent()
392         };
393 }
394 
GetTextDirection(const std::string & content,LayoutWrapper * layoutWrapper)395 TextDirection MultipleParagraphLayoutAlgorithm::GetTextDirection(
396     const std::string& content, LayoutWrapper* layoutWrapper)
397 {
398     auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
399     CHECK_NULL_RETURN(textLayoutProperty, TextDirection::LTR);
400 
401     auto direction = textLayoutProperty->GetLayoutDirection();
402     if (direction == TextDirection::LTR || direction == TextDirection::RTL) {
403         return direction;
404     }
405 
406     return GetTextDirectionByContent(content);
407 }
408 
GetTextDirectionByContent(const std::string & content)409 TextDirection MultipleParagraphLayoutAlgorithm::GetTextDirectionByContent(const std::string& content)
410 {
411     bool isRTL = AceApplicationInfo::GetInstance().IsRightToLeft();
412     auto textDirection = isRTL ? TextDirection::RTL : TextDirection::LTR;
413     auto showingTextForWString = StringUtils::ToWstring(content);
414     for (const auto& charOfShowingText : showingTextForWString) {
415         if (TextLayoutadapter::IsLeftToRight(charOfShowingText)) {
416             return TextDirection::LTR;
417         } else if (TextLayoutadapter::IsRightToLeft(charOfShowingText)) {
418             return TextDirection::RTL;
419         } else if (TextLayoutadapter::IsRightTOLeftArabic(charOfShowingText)) {
420             return TextDirection::RTL;
421         }
422     }
423     return textDirection;
424 }
425 
ParagraphReLayout(const LayoutConstraintF & contentConstraint)426 bool MultipleParagraphLayoutAlgorithm::ParagraphReLayout(const LayoutConstraintF& contentConstraint)
427 {
428     ACE_TEXT_SCOPED_TRACE("ParagraphReLayout");
429     // Confirmed specification: The width of the text paragraph covers the width of the component, so this code is
430     // generally not allowed to be modified
431     CHECK_NULL_RETURN(paragraphManager_, false);
432     auto paragraphs = paragraphManager_->GetParagraphs();
433     float paragraphNewWidth =
434         std::min(std::min(paragraphManager_->GetTextWidthIncludeIndent(), paragraphManager_->GetMaxWidth()),
435             GetMaxMeasureSize(contentConstraint).Width());
436     paragraphNewWidth =
437         std::clamp(paragraphNewWidth, contentConstraint.minSize.Width(), contentConstraint.maxSize.Width());
438     if (!contentConstraint.selfIdealSize.Width()) {
439         for (auto pIter = paragraphs.begin(); pIter != paragraphs.end(); pIter++) {
440             auto paragraph = pIter->paragraph;
441             CHECK_NULL_RETURN(paragraph, false);
442             if (!NearEqual(paragraphNewWidth, paragraph->GetMaxWidth())) {
443                 OTHER_DURATION();
444                 paragraph->Layout(std::ceil(paragraphNewWidth));
445             }
446         }
447     }
448     return true;
449 }
450 
UpdateParagraphBySpan(LayoutWrapper * layoutWrapper,ParagraphStyle paraStyle,double maxWidth,const TextStyle & textStyle)451 bool MultipleParagraphLayoutAlgorithm::UpdateParagraphBySpan(LayoutWrapper* layoutWrapper,
452     ParagraphStyle paraStyle, double maxWidth, const TextStyle& textStyle)
453 {
454     CHECK_NULL_RETURN(layoutWrapper, false);
455     auto layoutProperty = layoutWrapper->GetLayoutProperty();
456     CHECK_NULL_RETURN(layoutProperty, false);
457     auto frameNode = layoutWrapper->GetHostNode();
458     CHECK_NULL_RETURN(frameNode, false);
459     const auto& layoutConstrain = layoutProperty->CreateChildConstraint();
460     auto placeHolderLayoutConstrain = layoutConstrain;
461     placeHolderLayoutConstrain.maxSize.SetHeight(Infinity<float>());
462     placeHolderLayoutConstrain.percentReference.SetHeight(0);
463     const auto& children = layoutWrapper->GetAllChildrenWithBuild();
464     auto iterItems = children.begin();
465     auto pattern = frameNode->GetPattern<TextPattern>();
466     CHECK_NULL_RETURN(pattern, false);
467     auto aiSpanMap = pattern->GetAISpanMap();
468     int32_t spanTextLength = 0;
469     std::vector<WeakPtr<FrameNode>> imageNodeList;
470     std::vector<CustomSpanPlaceholderInfo> customSpanPlaceholderInfo;
471     int32_t paragraphIndex = -1;
472     preParagraphsPlaceholderCount_ = 0;
473     currentParagraphPlaceholderCount_ = 0;
474     paragraphFontSize_ = paraStyle.fontSize;
475     auto maxLines = static_cast<int32_t>(paraStyle.maxLines);
476     for (auto&& group : spans_) {
477         ParagraphStyle spanParagraphStyle = paraStyle;
478         RefPtr<SpanItem> paraStyleSpanItem = GetParagraphStyleSpanItem(group);
479         if (paraStyleSpanItem) {
480             GetSpanParagraphStyle(layoutWrapper, paraStyleSpanItem, spanParagraphStyle);
481             if (paraStyleSpanItem->fontStyle->HasFontSize()) {
482                 spanParagraphStyle.fontSize = paraStyleSpanItem->fontStyle->GetFontSizeValue().ConvertToPxDistribute(
483                     textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
484             }
485         }
486         if (paraStyle.maxLines != UINT32_MAX && !spanStringHasMaxLines_ && isSpanStringMode_) {
487             if (!paragraphManager_->GetParagraphs().empty()) {
488                 maxLines -= static_cast<int32_t>(paragraphManager_->GetParagraphs().back().paragraph->GetLineCount());
489             }
490             spanParagraphStyle.maxLines = std::max(maxLines, 0);
491         }
492         auto&& paragraph = Paragraph::Create(spanParagraphStyle, FontCollection::Current());
493         CHECK_NULL_RETURN(paragraph, false);
494         auto paraStart = spanTextLength;
495         paragraphIndex++;
496         for (const auto& child : group) {
497             if (!child) {
498                 continue;
499             }
500             child->paragraphIndex = paragraphIndex;
501             child->SetTextPattern(pattern);
502             auto imageSpanItem = AceType::DynamicCast<ImageSpanItem>(child);
503             if (imageSpanItem) {
504                 if (iterItems == children.end() || !(*iterItems)) {
505                     continue;
506                 }
507                 AddImageToParagraph(imageSpanItem, (*iterItems), layoutConstrain, paragraph, spanTextLength, textStyle);
508                 auto imageNode = (*iterItems)->GetHostNode();
509                 imageNodeList.emplace_back(WeakClaim(RawPtr(imageNode)));
510                 iterItems++;
511             } else if (AceType::DynamicCast<CustomSpanItem>(child)) {
512                 CustomSpanPlaceholderInfo customSpanPlaceholder;
513                 customSpanPlaceholder.paragraphIndex = paragraphIndex;
514                 auto customSpanItem = AceType::DynamicCast<CustomSpanItem>(child);
515                 UpdateParagraphByCustomSpan(
516                     customSpanItem, layoutWrapper, paragraph, spanTextLength, customSpanPlaceholder);
517                 customSpanPlaceholderInfo.emplace_back(customSpanPlaceholder);
518             } else if (AceType::InstanceOf<PlaceholderSpanItem>(child)) {
519                 if (iterItems == children.end() || !(*iterItems)) {
520                     continue;
521                 }
522                 auto placeholderSpanItem = AceType::DynamicCast<PlaceholderSpanItem>(child);
523                 if (!placeholderSpanItem) {
524                     continue;
525                 }
526                 AddPlaceHolderToParagraph(
527                     placeholderSpanItem, (*iterItems), placeHolderLayoutConstrain, paragraph, spanTextLength);
528                 iterItems++;
529             } else if (child->unicode != 0) {
530                 AddSymbolSpanToParagraph(child, spanTextLength, frameNode, paragraph);
531             } else {
532                 child->aiSpanMap = aiSpanMap;
533                 AddTextSpanToParagraph(child, spanTextLength, frameNode, paragraph);
534                 aiSpanMap = child->aiSpanMap;
535             }
536         }
537         preParagraphsPlaceholderCount_ += currentParagraphPlaceholderCount_;
538         currentParagraphPlaceholderCount_ = 0;
539         shadowOffset_ += GetShadowOffset(group);
540         HandleEmptyParagraph(paragraph, group);
541         paragraph->Build();
542         ApplyIndent(spanParagraphStyle, paragraph, maxWidth, textStyle);
543         UpdateSymbolSpanEffect(frameNode, paragraph, group);
544         if (paraStyle.maxLines != UINT32_MAX && !spanStringHasMaxLines_ && isSpanStringMode_) {
545             paragraph->Layout(static_cast<float>(maxWidth));
546         }
547         paragraphManager_->AddParagraph({ .paragraph = paragraph,
548             .paragraphStyle = spanParagraphStyle,
549             .start = paraStart,
550             .end = spanTextLength });
551     }
552     pattern->SetImageSpanNodeList(imageNodeList);
553     pattern->InitCustomSpanPlaceholderInfo(customSpanPlaceholderInfo);
554     return true;
555 }
556 
AddSymbolSpanToParagraph(const RefPtr<SpanItem> & child,int32_t & spanTextLength,const RefPtr<FrameNode> & frameNode,const RefPtr<Paragraph> & paragraph)557 void MultipleParagraphLayoutAlgorithm::AddSymbolSpanToParagraph(const RefPtr<SpanItem>& child, int32_t& spanTextLength,
558     const RefPtr<FrameNode>& frameNode, const RefPtr<Paragraph>& paragraph)
559 {
560     child->SetIsParentText(frameNode->GetTag() == V2::TEXT_ETS_TAG);
561     child->UpdateSymbolSpanParagraph(frameNode, paragraph);
562     spanTextLength += SYMBOL_SPAN_LENGTH;
563     child->length = SYMBOL_SPAN_LENGTH;
564     child->position = spanTextLength;
565     child->content = "  ";
566 }
567 
AddTextSpanToParagraph(const RefPtr<SpanItem> & child,int32_t & spanTextLength,const RefPtr<FrameNode> & frameNode,const RefPtr<Paragraph> & paragraph)568 void MultipleParagraphLayoutAlgorithm::AddTextSpanToParagraph(const RefPtr<SpanItem>& child, int32_t& spanTextLength,
569     const RefPtr<FrameNode>& frameNode, const RefPtr<Paragraph>& paragraph)
570 {
571     child->length = StringUtils::ToWstring(child->content).length();
572     spanTextLength += static_cast<int32_t>(child->length);
573     child->position = spanTextLength;
574     child->UpdateParagraph(frameNode, paragraph, isSpanStringMode_, PlaceholderStyle(), isMarquee_);
575 }
576 
AddImageToParagraph(RefPtr<ImageSpanItem> & imageSpanItem,const RefPtr<LayoutWrapper> & layoutWrapper,const LayoutConstraintF & layoutConstrain,const RefPtr<Paragraph> & paragraph,int32_t & spanTextLength,const TextStyle & textStyle)577 void MultipleParagraphLayoutAlgorithm::AddImageToParagraph(RefPtr<ImageSpanItem>& imageSpanItem,
578     const RefPtr<LayoutWrapper>& layoutWrapper, const LayoutConstraintF& layoutConstrain,
579     const RefPtr<Paragraph>& paragraph, int32_t& spanTextLength, const TextStyle& textStyle)
580 {
581     auto frameNode = layoutWrapper->GetHostNode();
582     CHECK_NULL_VOID(frameNode);
583     auto id = frameNode->GetId();
584     int32_t targetId = imageSpanItem->imageNodeId;
585     if (!isSpanStringMode_) {
586         CHECK_NULL_VOID(id == targetId);
587     }
588     layoutWrapper->Measure(layoutConstrain);
589     PlaceholderStyle placeholderStyle;
590     auto baselineOffset = Dimension(0.0f);
591     auto imageLayoutProperty = DynamicCast<ImageLayoutProperty>(layoutWrapper->GetLayoutProperty());
592     if (imageLayoutProperty) {
593         placeholderStyle.verticalAlign = imageLayoutProperty->GetVerticalAlign().value_or(VerticalAlign::BOTTOM);
594         baselineOffset = imageLayoutProperty->GetBaselineOffset().value_or(Dimension(0.0f));
595     }
596     auto geometryNode = layoutWrapper->GetGeometryNode();
597     CHECK_NULL_VOID(geometryNode);
598     placeholderStyle.width = geometryNode->GetMarginFrameSize().Width();
599     placeholderStyle.height = geometryNode->GetMarginFrameSize().Height();
600     placeholderStyle.paragraphFontSize = Dimension(paragraphFontSize_);
601     placeholderStyle.paragraphTextColor = textStyle_.value_or(TextStyle()).GetTextColor();
602     if (NearZero(baselineOffset.Value())) {
603         imageSpanItem->placeholderIndex =
604             imageSpanItem->UpdateParagraph(frameNode, paragraph, isSpanStringMode_, placeholderStyle);
605     } else {
606         placeholderStyle.baselineOffset = baselineOffset.ConvertToPxDistribute(
607             textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
608         imageSpanItem->placeholderIndex =
609             imageSpanItem->UpdateParagraph(frameNode, paragraph, isSpanStringMode_, placeholderStyle);
610     }
611     currentParagraphPlaceholderCount_++;
612     imageSpanItem->placeholderIndex += preParagraphsPlaceholderCount_;
613     imageSpanItem->content = " ";
614     spanTextLength += 1;
615     imageSpanItem->position = spanTextLength;
616     imageSpanItem->length = 1;
617 }
618 
AddPlaceHolderToParagraph(RefPtr<PlaceholderSpanItem> & placeholderSpanItem,const RefPtr<LayoutWrapper> & layoutWrapper,const LayoutConstraintF & layoutConstrain,const RefPtr<Paragraph> & paragraph,int32_t & spanTextLength)619 void MultipleParagraphLayoutAlgorithm::AddPlaceHolderToParagraph(RefPtr<PlaceholderSpanItem>& placeholderSpanItem,
620     const RefPtr<LayoutWrapper>& layoutWrapper, const LayoutConstraintF& layoutConstrain,
621     const RefPtr<Paragraph>& paragraph, int32_t& spanTextLength)
622 {
623     auto frameNode = layoutWrapper->GetHostNode();
624     CHECK_NULL_VOID(frameNode);
625     auto id = frameNode->GetId();
626     int32_t targetId = placeholderSpanItem->placeholderSpanNodeId;
627     CHECK_NULL_VOID(id == targetId);
628     // find the Corresponding ImageNode for every ImageSpanItem
629     layoutWrapper->Measure(layoutConstrain);
630     auto geometryNode = layoutWrapper->GetGeometryNode();
631     CHECK_NULL_VOID(geometryNode);
632     PlaceholderStyle placeholderStyle;
633     placeholderStyle.width = geometryNode->GetMarginFrameSize().Width();
634     placeholderStyle.height = geometryNode->GetMarginFrameSize().Height();
635     placeholderStyle.verticalAlign = VerticalAlign::NONE;
636     placeholderSpanItem->placeholderIndex =
637         placeholderSpanItem->UpdateParagraph(frameNode, paragraph, isSpanStringMode_, placeholderStyle);
638     currentParagraphPlaceholderCount_++;
639     placeholderSpanItem->placeholderIndex += preParagraphsPlaceholderCount_;
640     placeholderSpanItem->content = " ";
641     spanTextLength += 1;
642     placeholderSpanItem->length = 1;
643     placeholderSpanItem->position = spanTextLength;
644 }
645 
UpdateParagraphByCustomSpan(RefPtr<CustomSpanItem> & customSpanItem,LayoutWrapper * layoutWrapper,const RefPtr<Paragraph> & paragraph,int32_t & spanTextLength,CustomSpanPlaceholderInfo & customSpanPlaceholder)646 void MultipleParagraphLayoutAlgorithm::UpdateParagraphByCustomSpan(RefPtr<CustomSpanItem>& customSpanItem,
647     LayoutWrapper* layoutWrapper, const RefPtr<Paragraph>& paragraph, int32_t& spanTextLength,
648     CustomSpanPlaceholderInfo& customSpanPlaceholder)
649 {
650     CHECK_NULL_VOID(layoutWrapper);
651     auto layoutProperty = layoutWrapper->GetLayoutProperty();
652     CHECK_NULL_VOID(layoutProperty);
653     auto context = PipelineBase::GetCurrentContextSafely();
654     CHECK_NULL_VOID(context);
655     auto theme = context->GetTheme<TextTheme>();
656     CHECK_NULL_VOID(theme);
657     auto fontSize = theme->GetTextStyle().GetFontSize().ConvertToVp() * context->GetFontScale();
658     auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutProperty);
659     auto fontSizeOpt = textLayoutProperty->GetFontSize();
660     if (fontSizeOpt.has_value()) {
661         fontSize = fontSizeOpt.value().ConvertToVp() * context->GetFontScale();
662     }
663     auto width = 0.0f;
664     auto height = 0.0f;
665     if (customSpanItem->onMeasure.has_value()) {
666         auto onMeasure = customSpanItem->onMeasure.value();
667         CustomSpanMetrics customSpanMetrics = onMeasure({ fontSize });
668         width = static_cast<float>(customSpanMetrics.width * context->GetDipScale());
669         height = static_cast<float>(
670             customSpanMetrics.height.value_or(fontSize / context->GetFontScale()) * context->GetDipScale());
671     }
672     PlaceholderStyle placeholderStyle;
673     placeholderStyle.width = width;
674     placeholderStyle.height = height;
675     placeholderStyle.verticalAlign = VerticalAlign::NONE;
676     customSpanItem->placeholderIndex =
677         customSpanItem->UpdateParagraph(nullptr, paragraph, isSpanStringMode_, placeholderStyle);
678     currentParagraphPlaceholderCount_++;
679     customSpanItem->placeholderIndex += preParagraphsPlaceholderCount_;
680     customSpanItem->content = " ";
681     spanTextLength += 1;
682     customSpanItem->length = 1;
683     customSpanItem->position = spanTextLength;
684     if (customSpanItem->onDraw.has_value()) {
685         customSpanPlaceholder.onDraw = customSpanItem->onDraw.value();
686     }
687     customSpanPlaceholder.customSpanIndex = customSpanItem->placeholderIndex;
688 }
689 
ApplyIndent(ParagraphStyle & paragraphStyle,const RefPtr<Paragraph> & paragraph,double width,const TextStyle & textStyle)690 void MultipleParagraphLayoutAlgorithm::ApplyIndent(
691     ParagraphStyle& paragraphStyle, const RefPtr<Paragraph>& paragraph, double width, const TextStyle& textStyle)
692 {
693     auto indentValue = paragraphStyle.indent;
694     CHECK_NULL_VOID(paragraph);
695     double value = 0.0;
696     if (GreatNotEqual(indentValue.Value(), 0.0)) {
697         // first line indent
698         auto pipeline = PipelineContext::GetCurrentContextSafely();
699         CHECK_NULL_VOID(pipeline);
700         if (indentValue.Unit() != DimensionUnit::PERCENT) {
701             value = indentValue.ConvertToPxDistribute(
702                 textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
703         } else {
704             value = static_cast<float>(width * indentValue.Value());
705             paragraphStyle.indent = Dimension(value);
706         }
707     }
708     auto indent = static_cast<float>(value);
709     auto leadingMarginValue = 0.0f;
710     std::vector<float> indents;
711     if (paragraphStyle.leadingMargin.has_value()) {
712         leadingMarginValue = paragraphStyle.leadingMargin->size.Width().ConvertToPxDistribute(
713             textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
714     }
715     indents.emplace_back(indent + leadingMarginValue);
716     indents.emplace_back(leadingMarginValue);
717     paragraph->SetIndents(indents);
718 }
719 
UpdateSymbolSpanEffect(RefPtr<FrameNode> & frameNode,const RefPtr<Paragraph> & paragraph,const std::list<RefPtr<SpanItem>> & spans)720 void MultipleParagraphLayoutAlgorithm::UpdateSymbolSpanEffect(
721     RefPtr<FrameNode>& frameNode, const RefPtr<Paragraph>& paragraph, const std::list<RefPtr<SpanItem>>& spans)
722 {
723     for (const auto& child : spans) {
724         if (!child || child->unicode == 0) {
725             continue;
726         }
727         if (child->GetTextStyle()->isSymbolGlyph_) {
728             paragraph->SetParagraphSymbolAnimation(frameNode);
729             return;
730         }
731     }
732 }
733 
GetMaxMeasureSize(const LayoutConstraintF & contentConstraint)734 SizeF MultipleParagraphLayoutAlgorithm::GetMaxMeasureSize(const LayoutConstraintF& contentConstraint)
735 {
736     auto maxSize = contentConstraint.selfIdealSize;
737     maxSize.UpdateIllegalSizeWithCheck(contentConstraint.maxSize);
738     return maxSize.ConvertToSizeT();
739 }
740 } // namespace OHOS::Ace::NG
741