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