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             auto customSpanItem = AceType::DynamicCast<CustomSpanItem>(child);
166             if (customSpanItem && !customSpanItem->isFrameNode) {
167                 continue;
168             }
169             if (AceType::InstanceOf<ImageSpanItem>(child) || AceType::InstanceOf<PlaceholderSpanItem>(child)) {
170                 placeholderIndex.emplace_back(child->placeholderIndex);
171             }
172         }
173     }
174 }
175 
GetSpanParagraphStyle(LayoutWrapper * layoutWrapper,const RefPtr<SpanItem> & spanItem,ParagraphStyle & pStyle)176 void MultipleParagraphLayoutAlgorithm::GetSpanParagraphStyle(
177     LayoutWrapper* layoutWrapper, const RefPtr<SpanItem>& spanItem, ParagraphStyle& pStyle)
178 {
179     const auto& lineStyle = spanItem->textLineStyle;
180     CHECK_NULL_VOID(lineStyle);
181     if (lineStyle->HasTextAlign()) {
182         pStyle.align = lineStyle->GetTextAlignValue();
183     }
184     if (lineStyle->HasMaxLines()) {
185         pStyle.maxLines = lineStyle->GetMaxLinesValue();
186     }
187     if (lineStyle->HasWordBreak()) {
188         pStyle.wordBreak = lineStyle->GetWordBreakValue();
189     }
190     if (lineStyle->HasEllipsisMode()) {
191         pStyle.ellipsisMode = lineStyle->GetEllipsisModeValue();
192     }
193     if (lineStyle->HasTextOverflow()) {
194         pStyle.textOverflow = lineStyle->GetTextOverflowValue();
195     }
196     if (lineStyle->HasTextIndent()) {
197         pStyle.indent = lineStyle->GetTextIndentValue();
198     }
199     if (lineStyle->HasLineBreakStrategy()) {
200         pStyle.lineBreakStrategy = lineStyle->GetLineBreakStrategyValue();
201     }
202     if (lineStyle->HasLeadingMargin()) {
203         pStyle.leadingMargin = lineStyle->GetLeadingMarginValue();
204     }
205     if (lineStyle->HasLineHeight()) {
206         pStyle.lineHeight = lineStyle->GetLineHeightValue();
207     }
208     if (layoutWrapper) {
209         pStyle.direction = GetTextDirection(spanItem->content, layoutWrapper);
210     } else {
211         pStyle.direction = GetTextDirectionByContent(spanItem->content);
212     }
213 }
214 
FontRegisterCallback(const RefPtr<FrameNode> & frameNode,const TextStyle & textStyle)215 void MultipleParagraphLayoutAlgorithm::FontRegisterCallback(
216     const RefPtr<FrameNode>& frameNode, const TextStyle& textStyle)
217 {
218     auto callback = [weakNode = WeakPtr<FrameNode>(frameNode)] {
219         auto frameNode = weakNode.Upgrade();
220         CHECK_NULL_VOID(frameNode);
221         frameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
222         auto pattern = frameNode->GetPattern<TextPattern>();
223         CHECK_NULL_VOID(pattern);
224         auto modifier = DynamicCast<TextContentModifier>(pattern->GetContentModifier());
225         CHECK_NULL_VOID(modifier);
226         modifier->SetFontReady(true);
227     };
228     auto pipeline = frameNode->GetContext();
229     CHECK_NULL_VOID(pipeline);
230     auto fontManager = pipeline->GetFontManager();
231     if (fontManager) {
232         bool isCustomFont = false;
233         for (const auto& familyName : textStyle.GetFontFamilies()) {
234             bool customFont = fontManager->RegisterCallbackNG(frameNode, familyName, callback);
235             if (customFont) {
236                 isCustomFont = true;
237             }
238         }
239         if (isCustomFont || fontManager->IsDefaultFontChanged()) {
240             auto pattern = frameNode->GetPattern<TextPattern>();
241             CHECK_NULL_VOID(pattern);
242             pattern->SetIsCustomFont(true);
243             auto modifier = DynamicCast<TextContentModifier>(pattern->GetContentModifier());
244             CHECK_NULL_VOID(modifier);
245             modifier->SetIsCustomFont(true);
246         }
247     }
248 }
249 
UpdateTextColorIfForeground(const RefPtr<FrameNode> & frameNode,TextStyle & textStyle)250 void MultipleParagraphLayoutAlgorithm::UpdateTextColorIfForeground(
251     const RefPtr<FrameNode>& frameNode, TextStyle& textStyle)
252 {
253     auto renderContext = frameNode->GetRenderContext();
254     if (renderContext->HasForegroundColor()) {
255         if (renderContext->GetForegroundColorValue().GetValue() != textStyle.GetTextColor().GetValue()) {
256             textStyle.SetTextColor(Color::FOREGROUND);
257         }
258     } else if (renderContext->HasForegroundColorStrategy()) {
259         textStyle.SetTextColor(Color::FOREGROUND);
260     }
261 }
262 
SetFontSizePropertyToModifier(const RefPtr<TextLayoutProperty> & layoutProperty,const RefPtr<TextContentModifier> & modifier,const TextStyle & textStyle)263 void MultipleParagraphLayoutAlgorithm::SetFontSizePropertyToModifier(const RefPtr<TextLayoutProperty>& layoutProperty,
264     const RefPtr<TextContentModifier>& modifier, const TextStyle& textStyle)
265 {
266     auto fontSize = layoutProperty->GetFontSize();
267     if (fontSize.has_value()) {
268         modifier->SetFontSize(fontSize.value(), textStyle);
269     } else {
270         // Reset modifier FontSize.
271         modifier->SetFontSize(textStyle.GetFontSize(), textStyle, true);
272     }
273     auto adaptMinFontSize = layoutProperty->GetAdaptMinFontSize();
274     if (adaptMinFontSize.has_value()) {
275         modifier->SetAdaptMinFontSize(adaptMinFontSize.value(), textStyle);
276     } else {
277         // Reset modifier MinFontSize.
278         modifier->SetAdaptMinFontSize(textStyle.GetAdaptMinFontSize(), textStyle, true);
279     }
280     auto adaptMaxFontSize = layoutProperty->GetAdaptMaxFontSize();
281     if (adaptMaxFontSize.has_value()) {
282         modifier->SetAdaptMaxFontSize(adaptMaxFontSize.value(), textStyle);
283     } else {
284         // Reset modifier MaxFontSize.
285         modifier->SetAdaptMaxFontSize(textStyle.GetAdaptMaxFontSize(), textStyle, true);
286     }
287 }
288 
SetDecorationPropertyToModifier(const RefPtr<TextLayoutProperty> & layoutProperty,const RefPtr<TextContentModifier> & modifier,const TextStyle & textStyle)289 void MultipleParagraphLayoutAlgorithm::SetDecorationPropertyToModifier(const RefPtr<TextLayoutProperty>& layoutProperty,
290     const RefPtr<TextContentModifier>& modifier, const TextStyle& textStyle)
291 {
292     auto textDecorationColor = layoutProperty->GetTextDecorationColor();
293     if (textDecorationColor.has_value()) {
294         modifier->SetTextDecorationColor(textDecorationColor.value());
295     } else {
296         modifier->SetTextDecorationColor(textStyle.GetTextDecorationColor(), true);
297     }
298     auto textDecoration = layoutProperty->GetTextDecoration();
299     if (textDecoration.has_value()) {
300         modifier->SetTextDecoration(textDecoration.value());
301     } else {
302         modifier->SetTextDecoration(textStyle.GetTextDecoration(), true);
303     }
304 }
305 
SetPropertyToModifier(const RefPtr<TextLayoutProperty> & layoutProperty,const RefPtr<TextContentModifier> & modifier,const TextStyle & textStyle)306 void MultipleParagraphLayoutAlgorithm::SetPropertyToModifier(const RefPtr<TextLayoutProperty>& layoutProperty,
307     const RefPtr<TextContentModifier>& modifier, const TextStyle& textStyle)
308 {
309     SetFontSizePropertyToModifier(layoutProperty, modifier, textStyle);
310     auto fontWeight = layoutProperty->GetFontWeight();
311     if (fontWeight.has_value()) {
312         modifier->SetFontWeight(fontWeight.value());
313     } else {
314         modifier->SetFontWeight(textStyle.GetFontWeight(), true);
315     }
316     auto textColor = layoutProperty->GetTextColor();
317     if (textColor.has_value()) {
318         modifier->SetTextColor(textColor.value());
319     } else {
320         modifier->SetTextColor(textStyle.GetTextColor(), true);
321     }
322     auto textShadow = layoutProperty->GetTextShadow();
323     if (textShadow.has_value()) {
324         modifier->SetTextShadow(textShadow.value());
325     } else {
326         modifier->SetTextShadow(textStyle.GetTextShadows());
327     }
328     SetDecorationPropertyToModifier(layoutProperty, modifier, textStyle);
329     auto baselineOffset = layoutProperty->GetBaselineOffset();
330     if (baselineOffset.has_value()) {
331         modifier->SetBaselineOffset(baselineOffset.value(), textStyle);
332     } else {
333         modifier->SetBaselineOffset(textStyle.GetBaselineOffset(), textStyle, true);
334     }
335     auto lineHeight = layoutProperty->GetLineHeight();
336     if (lineHeight.has_value()) {
337         if (lineHeight->Unit() == DimensionUnit::PERCENT) {
338             modifier->SetLineHeight(lineHeight.value(), textStyle, true);
339         } else {
340             modifier->SetLineHeight(lineHeight.value(), textStyle);
341         }
342     } else {
343         modifier->SetLineHeight(textStyle.GetLineHeight(), textStyle, true);
344     }
345 }
346 
GetSingleParagraph() const347 RefPtr<Paragraph> MultipleParagraphLayoutAlgorithm::GetSingleParagraph() const
348 {
349     CHECK_NULL_RETURN(paragraphManager_, nullptr);
350     CHECK_NULL_RETURN(!paragraphManager_->GetParagraphs().empty(), nullptr);
351     auto paragraphInfo = paragraphManager_->GetParagraphs().front();
352     auto paragraph = paragraphInfo.paragraph;
353     CHECK_NULL_RETURN(paragraph, nullptr);
354     return paragraph;
355 }
356 
SetContentOffset(LayoutWrapper * layoutWrapper)357 OffsetF MultipleParagraphLayoutAlgorithm::SetContentOffset(LayoutWrapper* layoutWrapper)
358 {
359     OffsetF contentOffset(0.0f, 0.0f);
360     CHECK_NULL_RETURN(layoutWrapper, contentOffset);
361 
362     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
363     const auto& padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
364     MinusPaddingToSize(padding, size);
365     auto left = padding.left.value_or(0);
366     auto top = padding.top.value_or(0);
367     auto paddingOffset = OffsetF(left, top);
368     auto align = Alignment::CENTER;
369     if (layoutWrapper->GetLayoutProperty()->GetPositionProperty()) {
370         align = layoutWrapper->GetLayoutProperty()->GetPositionProperty()->GetAlignment().value_or(align);
371     }
372 
373     const auto& content = layoutWrapper->GetGeometryNode()->GetContent();
374     if (content) {
375         contentOffset = Alignment::GetAlignPosition(size, content->GetRect().GetSize(), align) + paddingOffset;
376         content->SetOffset(contentOffset);
377     }
378     return contentOffset;
379 }
380 
GetParagraphStyle(const TextStyle & textStyle,const std::string & content,LayoutWrapper * layoutWrapper) const381 ParagraphStyle MultipleParagraphLayoutAlgorithm::GetParagraphStyle(
382     const TextStyle& textStyle, const std::string& content, LayoutWrapper* layoutWrapper) const
383 {
384     return { .direction = GetTextDirection(content, layoutWrapper),
385         .align = textStyle.GetTextAlign(),
386         .maxLines = static_cast<int32_t>(textStyle.GetMaxLines()) < 0 ? UINT32_MAX : textStyle.GetMaxLines(),
387         .fontLocale = Localization::GetInstance()->GetFontLocale(),
388         .wordBreak = textStyle.GetWordBreak(),
389         .ellipsisMode = textStyle.GetEllipsisMode(),
390         .lineBreakStrategy = textStyle.GetLineBreakStrategy(),
391         .textOverflow = textStyle.GetTextOverflow(),
392         .indent = textStyle.GetTextIndent()
393         };
394 }
395 
GetTextDirection(const std::string & content,LayoutWrapper * layoutWrapper)396 TextDirection MultipleParagraphLayoutAlgorithm::GetTextDirection(
397     const std::string& content, LayoutWrapper* layoutWrapper)
398 {
399     auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
400     CHECK_NULL_RETURN(textLayoutProperty, TextDirection::LTR);
401 
402     auto direction = textLayoutProperty->GetLayoutDirection();
403     if (direction == TextDirection::LTR || direction == TextDirection::RTL) {
404         return direction;
405     }
406 
407     return GetTextDirectionByContent(content);
408 }
409 
GetTextDirectionByContent(const std::string & content)410 TextDirection MultipleParagraphLayoutAlgorithm::GetTextDirectionByContent(const std::string& content)
411 {
412     bool isRTL = AceApplicationInfo::GetInstance().IsRightToLeft();
413     auto textDirection = isRTL ? TextDirection::RTL : TextDirection::LTR;
414     auto showingTextForWString = StringUtils::ToWstring(content);
415     for (const auto& charOfShowingText : showingTextForWString) {
416         if (TextLayoutadapter::IsLeftToRight(charOfShowingText)) {
417             return TextDirection::LTR;
418         } else if (TextLayoutadapter::IsRightToLeft(charOfShowingText)) {
419             return TextDirection::RTL;
420         } else if (TextLayoutadapter::IsRightTOLeftArabic(charOfShowingText)) {
421             return TextDirection::RTL;
422         }
423     }
424     return textDirection;
425 }
426 
ParagraphReLayout(const LayoutConstraintF & contentConstraint)427 bool MultipleParagraphLayoutAlgorithm::ParagraphReLayout(const LayoutConstraintF& contentConstraint)
428 {
429     ACE_TEXT_SCOPED_TRACE("ParagraphReLayout");
430     // Confirmed specification: The width of the text paragraph covers the width of the component, so this code is
431     // generally not allowed to be modified
432     CHECK_NULL_RETURN(paragraphManager_, false);
433     auto paragraphs = paragraphManager_->GetParagraphs();
434     float paragraphNewWidth =
435         std::min(std::min(paragraphManager_->GetTextWidthIncludeIndent(), paragraphManager_->GetMaxWidth()),
436             GetMaxMeasureSize(contentConstraint).Width());
437     paragraphNewWidth =
438         std::clamp(paragraphNewWidth, contentConstraint.minSize.Width(), contentConstraint.maxSize.Width());
439     if (!contentConstraint.selfIdealSize.Width()) {
440         for (auto pIter = paragraphs.begin(); pIter != paragraphs.end(); pIter++) {
441             auto paragraph = pIter->paragraph;
442             CHECK_NULL_RETURN(paragraph, false);
443             if (!NearEqual(paragraphNewWidth, paragraph->GetMaxWidth())) {
444                 OTHER_DURATION();
445                 paragraph->Layout(std::ceil(paragraphNewWidth));
446             }
447         }
448     }
449     return true;
450 }
451 
UpdateParagraphBySpan(LayoutWrapper * layoutWrapper,ParagraphStyle paraStyle,double maxWidth,const TextStyle & textStyle)452 bool MultipleParagraphLayoutAlgorithm::UpdateParagraphBySpan(LayoutWrapper* layoutWrapper,
453     ParagraphStyle paraStyle, double maxWidth, const TextStyle& textStyle)
454 {
455     CHECK_NULL_RETURN(layoutWrapper, false);
456     auto layoutProperty = layoutWrapper->GetLayoutProperty();
457     CHECK_NULL_RETURN(layoutProperty, false);
458     auto frameNode = layoutWrapper->GetHostNode();
459     CHECK_NULL_RETURN(frameNode, false);
460     const auto& layoutConstrain = layoutProperty->CreateChildConstraint();
461     auto placeHolderLayoutConstrain = layoutConstrain;
462     placeHolderLayoutConstrain.maxSize.SetHeight(Infinity<float>());
463     placeHolderLayoutConstrain.percentReference.SetHeight(0);
464     const auto& children = layoutWrapper->GetAllChildrenWithBuild();
465     auto iterItems = children.begin();
466     auto pattern = frameNode->GetPattern<TextPattern>();
467     CHECK_NULL_RETURN(pattern, false);
468     auto aiSpanMap = pattern->GetAISpanMap();
469     int32_t spanTextLength = 0;
470     std::vector<WeakPtr<FrameNode>> imageNodeList;
471     std::vector<CustomSpanPlaceholderInfo> customSpanPlaceholderInfo;
472     int32_t paragraphIndex = -1;
473     preParagraphsPlaceholderCount_ = 0;
474     currentParagraphPlaceholderCount_ = 0;
475     paragraphFontSize_ = paraStyle.fontSize;
476     auto maxLines = static_cast<int32_t>(paraStyle.maxLines);
477     for (auto&& group : spans_) {
478         ParagraphStyle spanParagraphStyle = paraStyle;
479         RefPtr<SpanItem> paraStyleSpanItem = GetParagraphStyleSpanItem(group);
480         if (paraStyleSpanItem) {
481             GetSpanParagraphStyle(layoutWrapper, paraStyleSpanItem, spanParagraphStyle);
482             if (paraStyleSpanItem->fontStyle->HasFontSize()) {
483                 spanParagraphStyle.fontSize = paraStyleSpanItem->fontStyle->GetFontSizeValue().ConvertToPxDistribute(
484                     textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
485             }
486         }
487         if (paraStyle.maxLines != UINT32_MAX && !spanStringHasMaxLines_ && isSpanStringMode_) {
488             if (!paragraphManager_->GetParagraphs().empty()) {
489                 maxLines -= static_cast<int32_t>(paragraphManager_->GetParagraphs().back().paragraph->GetLineCount());
490             }
491             spanParagraphStyle.maxLines = std::max(maxLines, 0);
492         }
493         auto&& paragraph = Paragraph::Create(spanParagraphStyle, FontCollection::Current());
494         CHECK_NULL_RETURN(paragraph, false);
495         auto paraStart = spanTextLength;
496         paragraphIndex++;
497         for (const auto& child : group) {
498             if (!child) {
499                 continue;
500             }
501             child->paragraphIndex = paragraphIndex;
502             child->SetTextPattern(pattern);
503             auto imageSpanItem = AceType::DynamicCast<ImageSpanItem>(child);
504             if (imageSpanItem) {
505                 if (iterItems == children.end() || !(*iterItems)) {
506                     continue;
507                 }
508                 AddImageToParagraph(imageSpanItem, (*iterItems), layoutConstrain, paragraph, spanTextLength, textStyle);
509                 auto imageNode = (*iterItems)->GetHostNode();
510                 imageNodeList.emplace_back(WeakClaim(RawPtr(imageNode)));
511                 iterItems++;
512             } else if (AceType::DynamicCast<CustomSpanItem>(child)) {
513                 CustomSpanPlaceholderInfo customSpanPlaceholder;
514                 customSpanPlaceholder.paragraphIndex = paragraphIndex;
515                 auto customSpanItem = AceType::DynamicCast<CustomSpanItem>(child);
516                 UpdateParagraphByCustomSpan(
517                     customSpanItem, layoutWrapper, paragraph, spanTextLength, customSpanPlaceholder);
518                 customSpanPlaceholderInfo.emplace_back(customSpanPlaceholder);
519                 if (customSpanItem->isFrameNode) {
520                     iterItems++; // CAPI custom span is frameNode,need to move the iterator backwards
521                 }
522             } else if (AceType::InstanceOf<PlaceholderSpanItem>(child)) {
523                 if (iterItems == children.end() || !(*iterItems)) {
524                     continue;
525                 }
526                 auto placeholderSpanItem = AceType::DynamicCast<PlaceholderSpanItem>(child);
527                 if (!placeholderSpanItem) {
528                     continue;
529                 }
530                 AddPlaceHolderToParagraph(
531                     placeholderSpanItem, (*iterItems), placeHolderLayoutConstrain, paragraph, spanTextLength);
532                 iterItems++;
533             } else if (child->unicode != 0) {
534                 AddSymbolSpanToParagraph(child, spanTextLength, frameNode, paragraph);
535             } else {
536                 child->aiSpanMap = aiSpanMap;
537                 AddTextSpanToParagraph(child, spanTextLength, frameNode, paragraph);
538                 aiSpanMap = child->aiSpanMap;
539             }
540         }
541         preParagraphsPlaceholderCount_ += currentParagraphPlaceholderCount_;
542         currentParagraphPlaceholderCount_ = 0;
543         shadowOffset_ += GetShadowOffset(group);
544         HandleEmptyParagraph(paragraph, group);
545         paragraph->Build();
546         ApplyIndent(spanParagraphStyle, paragraph, maxWidth, textStyle);
547         UpdateSymbolSpanEffect(frameNode, paragraph, group);
548         if (paraStyle.maxLines != UINT32_MAX && !spanStringHasMaxLines_ && isSpanStringMode_) {
549             paragraph->Layout(static_cast<float>(maxWidth));
550         }
551         paragraphManager_->AddParagraph({ .paragraph = paragraph,
552             .paragraphStyle = spanParagraphStyle,
553             .start = paraStart,
554             .end = spanTextLength });
555     }
556     pattern->SetImageSpanNodeList(imageNodeList);
557     pattern->InitCustomSpanPlaceholderInfo(customSpanPlaceholderInfo);
558     return true;
559 }
560 
AddSymbolSpanToParagraph(const RefPtr<SpanItem> & child,int32_t & spanTextLength,const RefPtr<FrameNode> & frameNode,const RefPtr<Paragraph> & paragraph)561 void MultipleParagraphLayoutAlgorithm::AddSymbolSpanToParagraph(const RefPtr<SpanItem>& child, int32_t& spanTextLength,
562     const RefPtr<FrameNode>& frameNode, const RefPtr<Paragraph>& paragraph)
563 {
564     child->SetIsParentText(frameNode->GetTag() == V2::TEXT_ETS_TAG);
565     child->UpdateSymbolSpanParagraph(frameNode, paragraph);
566     spanTextLength += SYMBOL_SPAN_LENGTH;
567     child->length = SYMBOL_SPAN_LENGTH;
568     child->position = spanTextLength;
569     child->content = "  ";
570 }
571 
AddTextSpanToParagraph(const RefPtr<SpanItem> & child,int32_t & spanTextLength,const RefPtr<FrameNode> & frameNode,const RefPtr<Paragraph> & paragraph)572 void MultipleParagraphLayoutAlgorithm::AddTextSpanToParagraph(const RefPtr<SpanItem>& child, int32_t& spanTextLength,
573     const RefPtr<FrameNode>& frameNode, const RefPtr<Paragraph>& paragraph)
574 {
575     child->length = StringUtils::ToWstring(child->content).length();
576     spanTextLength += static_cast<int32_t>(child->length);
577     child->position = spanTextLength;
578     child->UpdateParagraph(frameNode, paragraph, isSpanStringMode_, PlaceholderStyle(), isMarquee_);
579 }
580 
AddImageToParagraph(RefPtr<ImageSpanItem> & imageSpanItem,const RefPtr<LayoutWrapper> & layoutWrapper,const LayoutConstraintF & layoutConstrain,const RefPtr<Paragraph> & paragraph,int32_t & spanTextLength,const TextStyle & textStyle)581 void MultipleParagraphLayoutAlgorithm::AddImageToParagraph(RefPtr<ImageSpanItem>& imageSpanItem,
582     const RefPtr<LayoutWrapper>& layoutWrapper, const LayoutConstraintF& layoutConstrain,
583     const RefPtr<Paragraph>& paragraph, int32_t& spanTextLength, const TextStyle& textStyle)
584 {
585     auto frameNode = layoutWrapper->GetHostNode();
586     CHECK_NULL_VOID(frameNode);
587     auto id = frameNode->GetId();
588     int32_t targetId = imageSpanItem->imageNodeId;
589     if (!isSpanStringMode_) {
590         CHECK_NULL_VOID(id == targetId);
591     }
592     layoutWrapper->Measure(layoutConstrain);
593     PlaceholderStyle placeholderStyle;
594     auto baselineOffset = Dimension(0.0f);
595     auto imageLayoutProperty = DynamicCast<ImageLayoutProperty>(layoutWrapper->GetLayoutProperty());
596     if (imageLayoutProperty) {
597         placeholderStyle.verticalAlign = imageLayoutProperty->GetVerticalAlign().value_or(VerticalAlign::BOTTOM);
598         baselineOffset = imageLayoutProperty->GetBaselineOffset().value_or(Dimension(0.0f));
599     }
600     auto geometryNode = layoutWrapper->GetGeometryNode();
601     CHECK_NULL_VOID(geometryNode);
602     placeholderStyle.width = geometryNode->GetMarginFrameSize().Width();
603     placeholderStyle.height = geometryNode->GetMarginFrameSize().Height();
604     placeholderStyle.paragraphFontSize = Dimension(paragraphFontSize_);
605     placeholderStyle.paragraphTextColor = textStyle_.value_or(TextStyle()).GetTextColor();
606     if (NearZero(baselineOffset.Value())) {
607         imageSpanItem->placeholderIndex =
608             imageSpanItem->UpdateParagraph(frameNode, paragraph, isSpanStringMode_, placeholderStyle);
609     } else {
610         placeholderStyle.baselineOffset = baselineOffset.ConvertToPxDistribute(
611             textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
612         imageSpanItem->placeholderIndex =
613             imageSpanItem->UpdateParagraph(frameNode, paragraph, isSpanStringMode_, placeholderStyle);
614     }
615     currentParagraphPlaceholderCount_++;
616     imageSpanItem->placeholderIndex += preParagraphsPlaceholderCount_;
617     imageSpanItem->content = " ";
618     spanTextLength += 1;
619     imageSpanItem->position = spanTextLength;
620     imageSpanItem->length = 1;
621 }
622 
AddPlaceHolderToParagraph(RefPtr<PlaceholderSpanItem> & placeholderSpanItem,const RefPtr<LayoutWrapper> & layoutWrapper,const LayoutConstraintF & layoutConstrain,const RefPtr<Paragraph> & paragraph,int32_t & spanTextLength)623 void MultipleParagraphLayoutAlgorithm::AddPlaceHolderToParagraph(RefPtr<PlaceholderSpanItem>& placeholderSpanItem,
624     const RefPtr<LayoutWrapper>& layoutWrapper, const LayoutConstraintF& layoutConstrain,
625     const RefPtr<Paragraph>& paragraph, int32_t& spanTextLength)
626 {
627     auto frameNode = layoutWrapper->GetHostNode();
628     CHECK_NULL_VOID(frameNode);
629     auto id = frameNode->GetId();
630     int32_t targetId = placeholderSpanItem->placeholderSpanNodeId;
631     CHECK_NULL_VOID(id == targetId);
632     // find the Corresponding ImageNode for every ImageSpanItem
633     layoutWrapper->Measure(layoutConstrain);
634     auto geometryNode = layoutWrapper->GetGeometryNode();
635     CHECK_NULL_VOID(geometryNode);
636     PlaceholderStyle placeholderStyle;
637     placeholderStyle.width = geometryNode->GetMarginFrameSize().Width();
638     placeholderStyle.height = geometryNode->GetMarginFrameSize().Height();
639     placeholderStyle.verticalAlign = VerticalAlign::NONE;
640     placeholderSpanItem->placeholderIndex =
641         placeholderSpanItem->UpdateParagraph(frameNode, paragraph, isSpanStringMode_, placeholderStyle);
642     currentParagraphPlaceholderCount_++;
643     placeholderSpanItem->placeholderIndex += preParagraphsPlaceholderCount_;
644     placeholderSpanItem->content = " ";
645     spanTextLength += 1;
646     placeholderSpanItem->length = 1;
647     placeholderSpanItem->position = spanTextLength;
648 }
649 
UpdateParagraphByCustomSpan(RefPtr<CustomSpanItem> & customSpanItem,LayoutWrapper * layoutWrapper,const RefPtr<Paragraph> & paragraph,int32_t & spanTextLength,CustomSpanPlaceholderInfo & customSpanPlaceholder)650 void MultipleParagraphLayoutAlgorithm::UpdateParagraphByCustomSpan(RefPtr<CustomSpanItem>& customSpanItem,
651     LayoutWrapper* layoutWrapper, const RefPtr<Paragraph>& paragraph, int32_t& spanTextLength,
652     CustomSpanPlaceholderInfo& customSpanPlaceholder)
653 {
654     CHECK_NULL_VOID(layoutWrapper);
655     auto layoutProperty = layoutWrapper->GetLayoutProperty();
656     CHECK_NULL_VOID(layoutProperty);
657     auto context = PipelineBase::GetCurrentContextSafely();
658     CHECK_NULL_VOID(context);
659     auto theme = context->GetTheme<TextTheme>();
660     CHECK_NULL_VOID(theme);
661     auto fontSize = theme->GetTextStyle().GetFontSize().ConvertToVp() * context->GetFontScale();
662     auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutProperty);
663     auto fontSizeOpt = textLayoutProperty->GetFontSize();
664     if (fontSizeOpt.has_value()) {
665         fontSize = fontSizeOpt.value().ConvertToVp() * context->GetFontScale();
666     }
667     auto width = 0.0f;
668     auto height = 0.0f;
669     if (customSpanItem->onMeasure.has_value()) {
670         auto onMeasure = customSpanItem->onMeasure.value();
671         CustomSpanMetrics customSpanMetrics = onMeasure({ fontSize });
672         width = static_cast<float>(customSpanMetrics.width * context->GetDipScale());
673         height = static_cast<float>(
674             customSpanMetrics.height.value_or(fontSize / context->GetFontScale()) * context->GetDipScale());
675     }
676     PlaceholderStyle placeholderStyle;
677     placeholderStyle.width = width;
678     placeholderStyle.height = height;
679     placeholderStyle.verticalAlign = VerticalAlign::NONE;
680     customSpanItem->placeholderIndex =
681         customSpanItem->UpdateParagraph(nullptr, paragraph, isSpanStringMode_, placeholderStyle);
682     currentParagraphPlaceholderCount_++;
683     customSpanItem->placeholderIndex += preParagraphsPlaceholderCount_;
684     customSpanItem->content = " ";
685     spanTextLength += 1;
686     customSpanItem->length = 1;
687     customSpanItem->position = spanTextLength;
688     if (customSpanItem->onDraw.has_value()) {
689         customSpanPlaceholder.onDraw = customSpanItem->onDraw.value();
690     }
691     customSpanPlaceholder.customSpanIndex = customSpanItem->placeholderIndex;
692 }
693 
ApplyIndent(ParagraphStyle & paragraphStyle,const RefPtr<Paragraph> & paragraph,double width,const TextStyle & textStyle)694 void MultipleParagraphLayoutAlgorithm::ApplyIndent(
695     ParagraphStyle& paragraphStyle, const RefPtr<Paragraph>& paragraph, double width, const TextStyle& textStyle)
696 {
697     auto indentValue = paragraphStyle.indent;
698     CHECK_NULL_VOID(paragraph);
699     double value = 0.0;
700     if (GreatNotEqual(indentValue.Value(), 0.0)) {
701         // first line indent
702         auto pipeline = PipelineContext::GetCurrentContextSafely();
703         CHECK_NULL_VOID(pipeline);
704         if (indentValue.Unit() != DimensionUnit::PERCENT) {
705             value = indentValue.ConvertToPxDistribute(
706                 textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
707         } else {
708             value = static_cast<float>(width * indentValue.Value());
709             paragraphStyle.indent = Dimension(value);
710         }
711     }
712     auto indent = static_cast<float>(value);
713     auto leadingMarginValue = 0.0f;
714     std::vector<float> indents;
715     if (paragraphStyle.leadingMargin.has_value()) {
716         leadingMarginValue = paragraphStyle.leadingMargin->size.Width().ConvertToPxDistribute(
717             textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
718     }
719     indents.emplace_back(indent + leadingMarginValue);
720     indents.emplace_back(leadingMarginValue);
721     paragraph->SetIndents(indents);
722 }
723 
UpdateSymbolSpanEffect(RefPtr<FrameNode> & frameNode,const RefPtr<Paragraph> & paragraph,const std::list<RefPtr<SpanItem>> & spans)724 void MultipleParagraphLayoutAlgorithm::UpdateSymbolSpanEffect(
725     RefPtr<FrameNode>& frameNode, const RefPtr<Paragraph>& paragraph, const std::list<RefPtr<SpanItem>>& spans)
726 {
727     for (const auto& child : spans) {
728         if (!child || child->unicode == 0) {
729             continue;
730         }
731         if (child->GetTextStyle()->isSymbolGlyph_) {
732             paragraph->SetParagraphSymbolAnimation(frameNode);
733             return;
734         }
735     }
736 }
737 
GetMaxMeasureSize(const LayoutConstraintF & contentConstraint)738 SizeF MultipleParagraphLayoutAlgorithm::GetMaxMeasureSize(const LayoutConstraintF& contentConstraint)
739 {
740     auto maxSize = contentConstraint.selfIdealSize;
741     maxSize.UpdateIllegalSizeWithCheck(contentConstraint.maxSize);
742     return maxSize.ConvertToSizeT();
743 }
744 } // namespace OHOS::Ace::NG
745