• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/text/text_layout_algorithm.h"
17 
18 #include <limits>
19 
20 #include "text_layout_adapter.h"
21 
22 #include "base/geometry/dimension.h"
23 #include "base/i18n/localization.h"
24 #include "base/utils/utils.h"
25 #include "core/common/container.h"
26 #include "core/common/font_manager.h"
27 #include "core/components/text/text_theme.h"
28 #include "core/components_ng/base/frame_node.h"
29 #include "core/components_ng/pattern/image/image_layout_property.h"
30 #include "core/components_ng/pattern/text/text_layout_property.h"
31 #include "core/components_ng/pattern/text/text_pattern.h"
32 #include "core/components_ng/render/drawing_prop_convertor.h"
33 #include "core/components_ng/render/font_collection.h"
34 #include "core/pipeline_ng/pipeline_context.h"
35 
36 namespace OHOS::Ace::NG {
37 namespace {
38 constexpr int32_t SYMBOL_SPAN_LENGTH = 2;
39 /**
40  * The baseline information needs to be calculated based on contentOffsetY.
41  */
GetContentOffsetY(LayoutWrapper * layoutWrapper)42 float GetContentOffsetY(LayoutWrapper* layoutWrapper)
43 {
44     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
45     const auto& padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
46     auto offsetY = padding.top.value_or(0);
47     auto align = Alignment::CENTER;
48     if (layoutWrapper->GetLayoutProperty()->GetPositionProperty()) {
49         align = layoutWrapper->GetLayoutProperty()->GetPositionProperty()->GetAlignment().value_or(align);
50     }
51     const auto& content = layoutWrapper->GetGeometryNode()->GetContent();
52     if (content) {
53         offsetY += Alignment::GetAlignPosition(size, content->GetRect().GetSize(), align).GetY();
54     }
55     return offsetY;
56 }
57 } // namespace
58 
59 TextLayoutAlgorithm::TextLayoutAlgorithm() = default;
60 
OnReset()61 void TextLayoutAlgorithm::OnReset() {}
62 
MeasureContent(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)63 std::optional<SizeF> TextLayoutAlgorithm::MeasureContent(
64     const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
65 {
66     if (Negative(contentConstraint.maxSize.Width()) || Negative(contentConstraint.maxSize.Height())) {
67         return std::nullopt;
68     }
69 
70     auto frameNode = layoutWrapper->GetHostNode();
71     CHECK_NULL_RETURN(frameNode, std::nullopt);
72     auto pipeline = frameNode->GetContext();
73     CHECK_NULL_RETURN(pipeline, std::nullopt);
74     auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
75     CHECK_NULL_RETURN(textLayoutProperty, std::nullopt);
76     auto pattern = frameNode->GetPattern<TextPattern>();
77     CHECK_NULL_RETURN(pattern, std::nullopt);
78     auto contentModifier = pattern->GetContentModifier();
79 
80     TextStyle textStyle = CreateTextStyleUsingTheme(
81         textLayoutProperty->GetFontStyle(), textLayoutProperty->GetTextLineStyle(), pipeline->GetTheme<TextTheme>());
82     if (contentModifier) {
83         SetPropertyToModifier(textLayoutProperty, contentModifier);
84         contentModifier->ModifyTextStyle(textStyle);
85         contentModifier->SetFontReady(false);
86     }
87     textStyle.SetHalfLeading(pipeline->GetHalfLeading());
88     // Register callback for fonts.
89     FontRegisterCallback(frameNode, textStyle);
90 
91     // Determines whether a foreground color is set or inherited.
92     UpdateTextColorIfForeground(frameNode, textStyle);
93 
94     if (textStyle.GetTextOverflow() == TextOverflow::MARQUEE) {
95         return BuildTextRaceParagraph(textStyle, textLayoutProperty, contentConstraint, pipeline, layoutWrapper);
96     }
97 
98     if (!AddPropertiesAndAnimations(textStyle, textLayoutProperty, contentConstraint, pipeline, layoutWrapper)) {
99         return std::nullopt;
100     }
101 
102     textStyle_ = textStyle;
103 
104     auto height = static_cast<float>(paragraph_->GetHeight());
105     double baselineOffset = 0.0;
106     textStyle.GetBaselineOffset().NormalizeToPx(
107         pipeline->GetDipScale(), pipeline->GetFontScale(), pipeline->GetLogicScale(), 0.0f, baselineOffset);
108 
109     baselineOffset_ = static_cast<float>(baselineOffset);
110     auto heightFinal = static_cast<float>(height + std::fabs(baselineOffset));
111     if (contentConstraint.selfIdealSize.Height().has_value()) {
112         heightFinal = std::min(
113             static_cast<float>(height + std::fabs(baselineOffset)), contentConstraint.selfIdealSize.Height().value());
114     } else {
115         heightFinal =
116             std::min(static_cast<float>(height + std::fabs(baselineOffset)), contentConstraint.maxSize.Height());
117     }
118     if (NearZero(contentConstraint.maxSize.Height()) || NearZero(contentConstraint.maxSize.Width())) {
119         return SizeF {};
120     }
121     if (frameNode->GetTag() == V2::TEXT_ETS_TAG && textLayoutProperty->GetContent().value_or("").empty() &&
122         NonPositive(static_cast<double>(paragraph_->GetLongestLine()))) {
123         // text content is empty
124         ACE_SCOPED_TRACE("TextHeightFinal [%f], TextContentWidth [%f], FontSize [%lf]",
125             heightFinal, paragraph_->GetMaxWidth(), textStyle.GetFontSize().ConvertToPx());
126         return SizeF {};
127     }
128     return SizeF(paragraph_->GetMaxWidth(), heightFinal);
129 }
130 
AddPropertiesAndAnimations(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & textLayoutProperty,const LayoutConstraintF & contentConstraint,const RefPtr<PipelineContext> & pipeline,LayoutWrapper * layoutWrapper)131 bool TextLayoutAlgorithm::AddPropertiesAndAnimations(TextStyle& textStyle,
132     const RefPtr<TextLayoutProperty>& textLayoutProperty, const LayoutConstraintF& contentConstraint,
133     const RefPtr<PipelineContext>& pipeline, LayoutWrapper* layoutWrapper)
134 {
135     bool result = false;
136     switch (textLayoutProperty->GetHeightAdaptivePolicyValue(TextHeightAdaptivePolicy::MAX_LINES_FIRST)) {
137         case TextHeightAdaptivePolicy::MAX_LINES_FIRST:
138             result = BuildParagraph(textStyle, textLayoutProperty, contentConstraint, pipeline, layoutWrapper);
139             break;
140         case TextHeightAdaptivePolicy::MIN_FONT_SIZE_FIRST:
141             result = BuildParagraphAdaptUseMinFontSize(
142                 textStyle, textLayoutProperty, contentConstraint, pipeline, layoutWrapper);
143             break;
144         case TextHeightAdaptivePolicy::LAYOUT_CONSTRAINT_FIRST:
145             result = BuildParagraphAdaptUseLayoutConstraint(
146                 textStyle, textLayoutProperty, contentConstraint, pipeline, layoutWrapper);
147             break;
148         default:
149             break;
150     }
151     return result;
152 }
153 
FontRegisterCallback(const RefPtr<FrameNode> & frameNode,const TextStyle & textStyle)154 void TextLayoutAlgorithm::FontRegisterCallback(const RefPtr<FrameNode>& frameNode, const TextStyle& textStyle)
155 {
156     auto callback = [weakNode = WeakPtr<FrameNode>(frameNode)] {
157         auto frameNode = weakNode.Upgrade();
158         CHECK_NULL_VOID(frameNode);
159         frameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
160         auto pattern = frameNode->GetPattern<TextPattern>();
161         CHECK_NULL_VOID(pattern);
162         auto modifier = DynamicCast<TextContentModifier>(pattern->GetContentModifier());
163         CHECK_NULL_VOID(modifier);
164         modifier->SetFontReady(true);
165     };
166     auto pipeline = frameNode->GetContext();
167     CHECK_NULL_VOID(pipeline);
168     auto fontManager = pipeline->GetFontManager();
169     if (fontManager) {
170         bool isCustomFont = false;
171         for (const auto& familyName : textStyle.GetFontFamilies()) {
172             bool customFont = fontManager->RegisterCallbackNG(frameNode, familyName, callback);
173             if (customFont) {
174                 isCustomFont = true;
175             }
176         }
177         fontManager->AddVariationNodeNG(frameNode);
178         if (isCustomFont || fontManager->IsDefaultFontChanged()) {
179             auto pattern = frameNode->GetPattern<TextPattern>();
180             CHECK_NULL_VOID(pattern);
181             pattern->SetIsCustomFont(true);
182             auto modifier = DynamicCast<TextContentModifier>(pattern->GetContentModifier());
183             CHECK_NULL_VOID(modifier);
184             modifier->SetIsCustomFont(true);
185         }
186     }
187 }
188 
Measure(LayoutWrapper * layoutWrapper)189 void TextLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
190 {
191     BoxLayoutAlgorithm::Measure(layoutWrapper);
192     auto baselineDistance = 0.0f;
193     if (paragraph_) {
194         baselineDistance = paragraph_->GetAlphabeticBaseline() + std::max(GetBaselineOffset(), 0.0f);
195     }
196     if (!NearZero(baselineDistance, 0.0f)) {
197         baselineDistance += GetContentOffsetY(layoutWrapper);
198     }
199     layoutWrapper->GetGeometryNode()->SetBaselineDistance(baselineDistance);
200 }
201 
UpdateParagraph(LayoutWrapper * layoutWrapper)202 void TextLayoutAlgorithm::UpdateParagraph(LayoutWrapper* layoutWrapper)
203 {
204     int32_t spanTextLength = GetPreviousLength();
205     CHECK_NULL_VOID(layoutWrapper);
206     auto layoutProperty = layoutWrapper->GetLayoutProperty();
207     CHECK_NULL_VOID(layoutProperty);
208     auto frameNode = layoutWrapper->GetHostNode();
209     const auto& layoutConstrain = layoutProperty->CreateChildConstraint();
210     auto placeHolderLayoutConstrain = layoutConstrain;
211     placeHolderLayoutConstrain.maxSize.SetHeight(Infinity<float>());
212     placeHolderLayoutConstrain.percentReference.SetHeight(0);
213     const auto& children = layoutWrapper->GetAllChildrenWithBuild();
214     auto pattern = frameNode->GetPattern<TextPattern>();
215     CHECK_NULL_VOID(pattern);
216     auto aiSpanMap = pattern->GetAISpanMap();
217     for (const auto& child : spanItemChildren_) {
218         if (!child) {
219             continue;
220         }
221         auto imageSpanItem = AceType::DynamicCast<ImageSpanItem>(child);
222         if (imageSpanItem) {
223             int32_t targetId = imageSpanItem->imageNodeId;
224             auto iterItems = children.begin();
225             // find the Corresponding ImageNode for every ImageSpanItem
226             while (iterItems != children.end() && (*iterItems) && (*iterItems)->GetHostNode()->GetId() != targetId) {
227                 iterItems++;
228             }
229             if (iterItems == children.end() || !(*iterItems)) {
230                 continue;
231             }
232             (*iterItems)->Measure(layoutConstrain);
233             auto verticalAlign = VerticalAlign::BOTTOM;
234             auto imageLayoutProperty = DynamicCast<ImageLayoutProperty>((*iterItems)->GetLayoutProperty());
235             if (imageLayoutProperty) {
236                 verticalAlign = imageLayoutProperty->GetVerticalAlign().value_or(VerticalAlign::BOTTOM);
237             }
238             auto geometryNode = (*iterItems)->GetGeometryNode();
239             if (!geometryNode) {
240                 iterItems++;
241                 continue;
242             }
243             auto width = geometryNode->GetMarginFrameSize().Width();
244             auto height = geometryNode->GetMarginFrameSize().Height();
245             child->placeholderIndex = child->UpdateParagraph(frameNode, paragraph_, width, height, verticalAlign);
246             child->content = " ";
247             child->position = spanTextLength + 1;
248             spanTextLength += 1;
249             iterItems++;
250         } else if (AceType::InstanceOf<PlaceholderSpanItem>(child)) {
251             auto placeholderSpanItem = AceType::DynamicCast<PlaceholderSpanItem>(child);
252             if (!placeholderSpanItem) {
253                 continue;
254             }
255             int32_t targetId = placeholderSpanItem->placeholderSpanNodeId;
256             auto iterItems = children.begin();
257             // find the Corresponding ImageNode for every ImageSpanItem
258             while (iterItems != children.end() && (*iterItems) && (*iterItems)->GetHostNode()->GetId() != targetId) {
259                 iterItems++;
260             }
261             if (iterItems == children.end() || !(*iterItems)) {
262                 continue;
263             }
264             (*iterItems)->Measure(placeHolderLayoutConstrain);
265             auto geometryNode = (*iterItems)->GetGeometryNode();
266             if (!geometryNode) {
267                 iterItems++;
268                 continue;
269             }
270             auto width = geometryNode->GetMarginFrameSize().Width();
271             auto height = geometryNode->GetMarginFrameSize().Height();
272             child->placeholderIndex = child->UpdateParagraph(frameNode, paragraph_, width, height, VerticalAlign::NONE);
273             child->content = " ";
274             child->position = spanTextLength + 1;
275             spanTextLength += 1;
276             iterItems++;
277         } else if (child->unicode != 0) {
278             child->SetIsParentText(frameNode->GetTag() == V2::TEXT_ETS_TAG);
279             child->UpdateSymbolSpanParagraph(frameNode, paragraph_);
280             child->position = spanTextLength + SYMBOL_SPAN_LENGTH;
281             child->content = "  ";
282             spanTextLength += SYMBOL_SPAN_LENGTH;
283         } else {
284             child->aiSpanMap = aiSpanMap;
285             child->UpdateParagraph(frameNode, paragraph_);
286             aiSpanMap = child->aiSpanMap;
287             child->position = spanTextLength + StringUtils::ToWstring(child->content).length();
288             spanTextLength += StringUtils::ToWstring(child->content).length();
289         }
290     }
291 }
292 
UpdateParagraphForAISpan(const TextStyle & textStyle,LayoutWrapper * layoutWrapper)293 void TextLayoutAlgorithm::UpdateParagraphForAISpan(const TextStyle& textStyle, LayoutWrapper* layoutWrapper)
294 {
295     CHECK_NULL_VOID(layoutWrapper);
296     auto layoutProperty = layoutWrapper->GetLayoutProperty();
297     CHECK_NULL_VOID(layoutProperty);
298     auto frameNode = layoutWrapper->GetHostNode();
299     CHECK_NULL_VOID(frameNode);
300     auto pipeline = frameNode->GetContext();
301     CHECK_NULL_VOID(pipeline);
302     auto pattern = frameNode->GetPattern<TextPattern>();
303     CHECK_NULL_VOID(pattern);
304     auto textForAI = pattern->GetTextForAI();
305     auto wTextForAI = StringUtils::ToWstring(textForAI);
306     int32_t wTextForAILength = static_cast<int32_t>(wTextForAI.length());
307     int32_t preEnd = 0;
308     DragSpanPosition dragSpanPosition;
309     dragSpanPosition.dragStart = pattern->GetRecoverStart();
310     dragSpanPosition.dragEnd = pattern->GetRecoverEnd();
311     bool isDragging = pattern->IsDragging();
312     TextStyle aiSpanTextStyle = textStyle;
313     aiSpanTextStyle.SetTextColor(Color::BLUE);
314     aiSpanTextStyle.SetTextDecoration(TextDecoration::UNDERLINE);
315     aiSpanTextStyle.SetTextDecorationColor(Color::BLUE);
316     for (auto kv : pattern->GetAISpanMap()) {
317         if (preEnd >= wTextForAILength) {
318             break;
319         }
320         auto aiSpan = kv.second;
321         if (aiSpan.start < preEnd) {
322             TAG_LOGI(AceLogTag::ACE_TEXT, "Error prediction");
323             continue;
324         }
325         if (preEnd < aiSpan.start) {
326             dragSpanPosition.spanStart = preEnd;
327             dragSpanPosition.spanEnd = aiSpan.start;
328             GrayDisplayAISpan(dragSpanPosition, wTextForAI, textStyle, isDragging);
329         }
330         preEnd = aiSpan.end;
331         dragSpanPosition.spanStart = aiSpan.start;
332         dragSpanPosition.spanEnd = aiSpan.end;
333         GrayDisplayAISpan(dragSpanPosition, wTextForAI, aiSpanTextStyle, isDragging);
334     }
335     if (preEnd < wTextForAILength) {
336         dragSpanPosition.spanStart = preEnd;
337         dragSpanPosition.spanEnd = wTextForAILength;
338         GrayDisplayAISpan(dragSpanPosition, wTextForAI, textStyle, isDragging);
339     }
340 }
341 
GrayDisplayAISpan(const DragSpanPosition & dragSpanPosition,const std::wstring wTextForAI,const TextStyle & textStyle,bool isDragging)342 void TextLayoutAlgorithm::GrayDisplayAISpan(const DragSpanPosition& dragSpanPosition,
343     const std::wstring wTextForAI, const TextStyle& textStyle, bool isDragging)
344 {
345     int32_t dragStart = dragSpanPosition.dragStart;
346     int32_t dragEnd = dragSpanPosition.dragEnd;
347     int32_t spanStart = dragSpanPosition.spanStart;
348     int32_t spanEnd = dragSpanPosition.spanEnd;
349     std::vector<std::string> contents = {};
350     std::string firstParagraph = "";
351     std::string secondParagraph = "";
352     std::string thirdParagraph = "";
353     if (dragStart > spanEnd || dragEnd < spanStart || !isDragging) {
354         firstParagraph = StringOutBoundProtection(spanStart, spanEnd - spanStart, wTextForAI);
355     } else if (spanStart <= dragStart && spanEnd >= dragStart && spanEnd <= dragEnd) {
356         firstParagraph = StringOutBoundProtection(spanStart, dragStart - spanStart, wTextForAI);
357         secondParagraph = StringOutBoundProtection(dragStart, spanEnd - dragStart, wTextForAI);
358     } else if (spanStart >= dragStart && spanEnd <= dragEnd) {
359         secondParagraph = StringOutBoundProtection(spanStart, spanEnd - spanStart, wTextForAI);
360     } else if (spanStart <= dragStart && spanEnd >= dragEnd) {
361         firstParagraph = StringOutBoundProtection(spanStart, dragStart - spanStart, wTextForAI);
362         secondParagraph = StringOutBoundProtection(dragStart, dragEnd - dragStart, wTextForAI);
363         thirdParagraph = StringOutBoundProtection(dragEnd, spanEnd - dragEnd, wTextForAI);
364     } else {
365         secondParagraph = StringOutBoundProtection(spanStart, dragEnd - spanStart, wTextForAI);
366         thirdParagraph = StringOutBoundProtection(dragEnd, spanEnd - dragEnd, wTextForAI);
367     }
368     contents = { firstParagraph, secondParagraph, thirdParagraph };
369     CreateParagraphDrag(textStyle, contents);
370 }
371 
StringOutBoundProtection(int32_t position,int32_t length,std::wstring wTextForAI)372 std::string TextLayoutAlgorithm::StringOutBoundProtection(int32_t position, int32_t length, std::wstring wTextForAI)
373 {
374     int32_t wTextForAILength = static_cast<int32_t>(wTextForAI.length());
375     if (position >= wTextForAILength || length > wTextForAILength - position) {
376         return "";
377     } else {
378         return StringUtils::ToString(wTextForAI.substr(position, length));
379     }
380 }
381 
CreateParagraph(const TextStyle & textStyle,std::string content,LayoutWrapper * layoutWrapper)382 bool TextLayoutAlgorithm::CreateParagraph(const TextStyle& textStyle, std::string content, LayoutWrapper* layoutWrapper)
383 {
384     auto frameNode = layoutWrapper->GetHostNode();
385     auto pipeline = frameNode->GetContext();
386     auto pattern = frameNode->GetPattern<TextPattern>();
387     auto paraStyle = GetParagraphStyle(textStyle, content, layoutWrapper);
388     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) && spanItemChildren_.empty()) {
389         paraStyle.fontSize = textStyle.GetFontSize().ConvertToPx();
390     }
391     paragraph_ = Paragraph::Create(paraStyle, FontCollection::Current());
392     CHECK_NULL_RETURN(paragraph_, false);
393     if (frameNode->GetTag() == V2::SYMBOL_ETS_TAG) {
394         auto layoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
395         CHECK_NULL_RETURN(layoutProperty, false);
396         auto symbolSourceInfo = layoutProperty->GetSymbolSourceInfo();
397         CHECK_NULL_RETURN(symbolSourceInfo, false);
398         TextStyle symbolTextStyle = textStyle;
399         symbolTextStyle.isSymbolGlyph_ = true;
400         symbolTextStyle.SetRenderStrategy(
401             symbolTextStyle.GetRenderStrategy() < 0 ? 0 : symbolTextStyle.GetRenderStrategy());
402         symbolTextStyle.SetEffectStrategy(
403             symbolTextStyle.GetEffectStrategy() < 0 ? 0 : symbolTextStyle.GetEffectStrategy());
404         paragraph_->PushStyle(symbolTextStyle);
405         paragraph_->AddSymbol(symbolSourceInfo->GetUnicode());
406         paragraph_->PopStyle();
407         paragraph_->Build();
408         paragraph_->SetParagraphSymbolAnimation(frameNode);
409         return true;
410     }
411     paragraph_->PushStyle(textStyle);
412     CHECK_NULL_RETURN(pattern, -1);
413     if (spanItemChildren_.empty()) {
414         if (pattern->NeedShowAIDetect()) {
415             UpdateParagraphForAISpan(textStyle, layoutWrapper);
416         } else {
417             if (pattern->IsDragging()) {
418                 auto dragContents = pattern->GetDragContents();
419                 CreateParagraphDrag(textStyle, dragContents);
420             } else {
421                 StringUtils::TransformStrCase(content, static_cast<int32_t>(textStyle.GetTextCase()));
422                 paragraph_->AddText(StringUtils::Str8ToStr16(content));
423             }
424         }
425     } else {
426         UpdateParagraph(layoutWrapper);
427     }
428     paragraph_->Build();
429     UpdateSymbolSpanEffect(frameNode);
430     return true;
431 }
432 
UpdateSymbolSpanEffect(RefPtr<FrameNode> & frameNode)433 void TextLayoutAlgorithm::UpdateSymbolSpanEffect(RefPtr<FrameNode>& frameNode)
434 {
435     for (const auto& child : spanItemChildren_) {
436         if (!child || child->unicode == 0) {
437             continue;
438         }
439         if (child->GetTextStyle()->isSymbolGlyph_) {
440             paragraph_->SetParagraphSymbolAnimation(frameNode);
441             return;
442         }
443     }
444 }
445 
CreateParagraphDrag(const TextStyle & textStyle,const std::vector<std::string> & contents)446 void TextLayoutAlgorithm::CreateParagraphDrag(const TextStyle& textStyle, const std::vector<std::string>& contents)
447 {
448     TextStyle dragTextStyle = textStyle;
449     Color color = textStyle.GetTextColor().ChangeAlpha(DRAGGED_TEXT_TRANSPARENCY);
450     dragTextStyle.SetTextColor(color);
451     std::vector<TextStyle> textStyles { textStyle, dragTextStyle, textStyle };
452 
453     for (size_t i = 0; i < contents.size(); i++) {
454         std::string splitStr = contents[i];
455         if (splitStr.empty()) {
456             continue;
457         }
458         auto& style = textStyles[i];
459         paragraph_->PushStyle(style);
460         StringUtils::TransformStrCase(splitStr, static_cast<int32_t>(style.GetTextCase()));
461         paragraph_->AddText(StringUtils::Str8ToStr16(splitStr));
462         paragraph_->PopStyle();
463     }
464 }
465 
CreateParagraphAndLayout(const TextStyle & textStyle,const std::string & content,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)466 bool TextLayoutAlgorithm::CreateParagraphAndLayout(const TextStyle& textStyle, const std::string& content,
467     const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
468 {
469     if (!CreateParagraph(textStyle, content, layoutWrapper)) {
470         return false;
471     }
472     CHECK_NULL_RETURN(paragraph_, false);
473     auto maxSize = GetMaxMeasureSize(contentConstraint);
474     ApplyIndent(textStyle, maxSize.Width());
475     paragraph_->Layout(maxSize.Width());
476 
477     bool ret = CreateImageSpanAndLayout(textStyle, content, contentConstraint, layoutWrapper);
478     return ret;
479 }
480 
GetContentOffset(LayoutWrapper * layoutWrapper)481 OffsetF TextLayoutAlgorithm::GetContentOffset(LayoutWrapper* layoutWrapper)
482 {
483     OffsetF contentOffset(0.0, 0.0);
484     CHECK_NULL_RETURN(layoutWrapper, contentOffset);
485 
486     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
487     const auto& padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
488     MinusPaddingToSize(padding, size);
489     auto left = padding.left.value_or(0);
490     auto top = padding.top.value_or(0);
491     auto paddingOffset = OffsetF(left, top);
492     auto align = Alignment::CENTER;
493     if (layoutWrapper->GetLayoutProperty()->GetPositionProperty()) {
494         align = layoutWrapper->GetLayoutProperty()->GetPositionProperty()->GetAlignment().value_or(align);
495     }
496 
497     const auto& content = layoutWrapper->GetGeometryNode()->GetContent();
498     if (content) {
499         contentOffset = Alignment::GetAlignPosition(size, content->GetRect().GetSize(), align) + paddingOffset;
500         content->SetOffset(contentOffset);
501     }
502     return contentOffset;
503 }
504 
Layout(LayoutWrapper * layoutWrapper)505 void TextLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
506 {
507     CHECK_NULL_VOID(layoutWrapper);
508     auto contentOffset = GetContentOffset(layoutWrapper);
509     std::vector<int32_t> placeholderIndex;
510     for (const auto& child : spanItemChildren_) {
511         if (!child) {
512             continue;
513         }
514         if (AceType::InstanceOf<ImageSpanItem>(child) || AceType::InstanceOf<PlaceholderSpanItem>(child)) {
515             placeholderIndex.emplace_back(child->placeholderIndex);
516         }
517     }
518     if (spanItemChildren_.empty() || placeholderIndex.empty()) {
519         return;
520     }
521 
522     size_t index = 0;
523     std::vector<RectF> rectsForPlaceholders;
524     GetPlaceholderRects(rectsForPlaceholders);
525     const auto& children = layoutWrapper->GetAllChildrenWithBuild();
526     // children only contains the image span.
527     for (const auto& child : children) {
528         if (!child) {
529             ++index;
530             continue;
531         }
532         if (index >= placeholderIndex.size() ||
533             (index >= rectsForPlaceholders.size() && child->GetHostTag() != V2::PLACEHOLDER_SPAN_ETS_TAG)) {
534             child->SetActive(false);
535             continue;
536         }
537         child->SetActive(true);
538         auto rect = rectsForPlaceholders.at(index) - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
539         auto geometryNode = child->GetGeometryNode();
540         if (!geometryNode) {
541             ++index;
542             continue;
543         }
544         geometryNode->SetMarginFrameOffset(contentOffset + OffsetF(rect.Left(), rect.Top()));
545         child->Layout();
546         ++index;
547     }
548 
549     auto frameNode = layoutWrapper->GetHostNode();
550     CHECK_NULL_VOID(frameNode);
551     auto pipeline = frameNode->GetContext();
552     CHECK_NULL_VOID(pipeline);
553     auto pattern = frameNode->GetPattern<TextPattern>();
554     CHECK_NULL_VOID(pattern);
555     pattern->InitSpanImageLayout(placeholderIndex, rectsForPlaceholders, contentOffset);
556 }
557 
AdaptMinTextSize(TextStyle & textStyle,const std::string & content,const LayoutConstraintF & contentConstraint,const RefPtr<PipelineContext> & pipeline,LayoutWrapper * layoutWrapper)558 bool TextLayoutAlgorithm::AdaptMinTextSize(TextStyle& textStyle, const std::string& content,
559     const LayoutConstraintF& contentConstraint, const RefPtr<PipelineContext>& pipeline, LayoutWrapper* layoutWrapper)
560 {
561     double maxFontSize = 0.0;
562     double minFontSize = 0.0;
563     if (!textStyle.GetAdaptMaxFontSize().NormalizeToPx(pipeline->GetDipScale(), pipeline->GetFontScale(),
564             pipeline->GetLogicScale(), contentConstraint.maxSize.Height(), maxFontSize)) {
565         return false;
566     }
567     if (!textStyle.GetAdaptMinFontSize().NormalizeToPx(pipeline->GetDipScale(), pipeline->GetFontScale(),
568             pipeline->GetLogicScale(), contentConstraint.maxSize.Height(), minFontSize)) {
569         return false;
570     }
571     if (LessNotEqual(maxFontSize, minFontSize) || LessOrEqual(minFontSize, 0.0)) {
572         if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) {
573             TAG_LOGE(AceLogTag::ACE_TEXT, "create paragraph error");
574             return false;
575         }
576         return true;
577     }
578     constexpr Dimension ADAPT_UNIT = 1.0_fp;
579     Dimension step = ADAPT_UNIT;
580     if (GreatNotEqual(textStyle.GetAdaptFontSizeStep().Value(), 0.0)) {
581         step = textStyle.GetAdaptFontSizeStep();
582     }
583     double stepSize = 0.0;
584     if (!step.NormalizeToPx(pipeline->GetDipScale(), pipeline->GetFontScale(), pipeline->GetLogicScale(),
585             contentConstraint.maxSize.Height(), stepSize)) {
586         return false;
587     }
588     auto maxSize = GetMaxMeasureSize(contentConstraint);
589     while (GreatOrEqual(maxFontSize, minFontSize)) {
590         textStyle.SetFontSize(Dimension(maxFontSize));
591         if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) {
592             TAG_LOGE(AceLogTag::ACE_TEXT, "create paragraph error");
593             return false;
594         }
595         if (!DidExceedMaxLines(maxSize)) {
596             break;
597         }
598         maxFontSize -= stepSize;
599     }
600     return true;
601 }
602 
DidExceedMaxLines(const SizeF & maxSize)603 bool TextLayoutAlgorithm::DidExceedMaxLines(const SizeF& maxSize)
604 {
605     CHECK_NULL_RETURN(paragraph_, false);
606     bool didExceedMaxLines = paragraph_->DidExceedMaxLines();
607     didExceedMaxLines = didExceedMaxLines || GreatNotEqual(paragraph_->GetHeight(), maxSize.Height());
608     didExceedMaxLines = didExceedMaxLines || GreatNotEqual(paragraph_->GetLongestLine(), maxSize.Width());
609     return didExceedMaxLines;
610 }
611 
GetTextDirection(const std::string & content,LayoutWrapper * layoutWrapper)612 TextDirection TextLayoutAlgorithm::GetTextDirection(const std::string& content, LayoutWrapper* layoutWrapper)
613 {
614     auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
615     CHECK_NULL_RETURN(textLayoutProperty, TextDirection::LTR);
616     auto direction = textLayoutProperty->GetLayoutDirection();
617     if (direction == TextDirection::LTR || direction == TextDirection::RTL) {
618         return direction;
619     }
620 
621     TextDirection textDirection = TextDirection::LTR;
622     auto showingTextForWString = StringUtils::ToWstring(content);
623     for (const auto& charOfShowingText : showingTextForWString) {
624         if (TextLayoutadapter::IsLeftToRight(charOfShowingText)) {
625             return TextDirection::LTR;
626         } else if (TextLayoutadapter::IsRightToLeft(charOfShowingText)) {
627             return TextDirection::RTL;
628         } else if (TextLayoutadapter::IsRightTOLeftArabic(charOfShowingText)) {
629             return TextDirection::RTL;
630         }
631     }
632     return textDirection;
633 }
634 
GetTextWidth() const635 float TextLayoutAlgorithm::GetTextWidth() const
636 {
637     CHECK_NULL_RETURN(paragraph_, 0.0);
638     return paragraph_->GetTextWidth();
639 }
640 
GetParagraph()641 const RefPtr<Paragraph>& TextLayoutAlgorithm::GetParagraph()
642 {
643     return paragraph_;
644 }
645 
GetBaselineOffset() const646 float TextLayoutAlgorithm::GetBaselineOffset() const
647 {
648     return baselineOffset_;
649 }
650 
GetMaxMeasureSize(const LayoutConstraintF & contentConstraint) const651 SizeF TextLayoutAlgorithm::GetMaxMeasureSize(const LayoutConstraintF& contentConstraint) const
652 {
653     auto maxSize = contentConstraint.selfIdealSize;
654     maxSize.UpdateIllegalSizeWithCheck(contentConstraint.maxSize);
655     return maxSize.ConvertToSizeT();
656 }
657 
GetSpanItemChildren()658 std::list<RefPtr<SpanItem>>&& TextLayoutAlgorithm::GetSpanItemChildren()
659 {
660     return std::move(spanItemChildren_);
661 }
662 
BuildParagraph(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,const RefPtr<PipelineContext> & pipeline,LayoutWrapper * layoutWrapper)663 bool TextLayoutAlgorithm::BuildParagraph(TextStyle& textStyle, const RefPtr<TextLayoutProperty>& layoutProperty,
664     const LayoutConstraintF& contentConstraint, const RefPtr<PipelineContext>& pipeline, LayoutWrapper* layoutWrapper)
665 {
666     if (!textStyle.GetAdaptTextSize()) {
667         if (!CreateParagraphAndLayout(
668                 textStyle, layoutProperty->GetContent().value_or(""), contentConstraint, layoutWrapper)) {
669             TAG_LOGE(AceLogTag::ACE_TEXT, "create paragraph error");
670             return false;
671         }
672     } else {
673         if (!AdaptMinTextSize(
674                 textStyle, layoutProperty->GetContent().value_or(""), contentConstraint, pipeline, layoutWrapper)) {
675             return false;
676         }
677     }
678 
679     // Confirmed specification: The width of the text paragraph covers the width of the component, so this code is
680     // generally not allowed to be modified
681     if (!contentConstraint.selfIdealSize.Width()) {
682         float paragraphNewWidth = std::min(std::min(GetTextWidth(), paragraph_->GetMaxWidth()) + indent_,
683             GetMaxMeasureSize(contentConstraint).Width());
684         paragraphNewWidth =
685             std::clamp(paragraphNewWidth, contentConstraint.minSize.Width(), contentConstraint.maxSize.Width());
686         if (!NearEqual(paragraphNewWidth, paragraph_->GetMaxWidth())) {
687             paragraph_->Layout(std::ceil(paragraphNewWidth));
688         }
689     }
690     return true;
691 }
692 
BuildParagraphAdaptUseMinFontSize(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,const RefPtr<PipelineContext> & pipeline,LayoutWrapper * layoutWrapper)693 bool TextLayoutAlgorithm::BuildParagraphAdaptUseMinFontSize(TextStyle& textStyle,
694     const RefPtr<TextLayoutProperty>& layoutProperty, const LayoutConstraintF& contentConstraint,
695     const RefPtr<PipelineContext>& pipeline, LayoutWrapper* layoutWrapper)
696 {
697     if (!AdaptMaxTextSize(
698             textStyle, layoutProperty->GetContent().value_or(""), contentConstraint, pipeline, layoutWrapper)) {
699         return false;
700     }
701 
702     // Confirmed specification: The width of the text paragraph covers the width of the component, so this code is
703     // generally not allowed to be modified
704     if (!contentConstraint.selfIdealSize.Width()) {
705         float paragraphNewWidth = std::min(std::min(GetTextWidth(), paragraph_->GetMaxWidth()) + indent_,
706             GetMaxMeasureSize(contentConstraint).Width());
707         paragraphNewWidth =
708             std::clamp(paragraphNewWidth, contentConstraint.minSize.Width(), contentConstraint.maxSize.Width());
709         if (!NearEqual(paragraphNewWidth, paragraph_->GetMaxWidth())) {
710             paragraph_->Layout(std::ceil(paragraphNewWidth));
711         }
712     }
713 
714     return true;
715 }
716 
BuildParagraphAdaptUseLayoutConstraint(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,const RefPtr<PipelineContext> & pipeline,LayoutWrapper * layoutWrapper)717 bool TextLayoutAlgorithm::BuildParagraphAdaptUseLayoutConstraint(TextStyle& textStyle,
718     const RefPtr<TextLayoutProperty>& layoutProperty, const LayoutConstraintF& contentConstraint,
719     const RefPtr<PipelineContext>& pipeline, LayoutWrapper* layoutWrapper)
720 {
721     // Create the paragraph and obtain the height.
722     if (!BuildParagraph(textStyle, layoutProperty, contentConstraint, pipeline, layoutWrapper)) {
723         return false;
724     }
725     auto height = static_cast<float>(paragraph_->GetHeight());
726     double minTextSizeHeight = 0.0;
727     textStyle.GetAdaptMinFontSize().NormalizeToPx(
728         pipeline->GetDipScale(), pipeline->GetFontScale(), pipeline->GetLogicScale(), height, minTextSizeHeight);
729     if (LessOrEqual(minTextSizeHeight, 0.0)) {
730         textStyle.GetFontSize().NormalizeToPx(
731             pipeline->GetDipScale(), pipeline->GetFontScale(), pipeline->GetLogicScale(), height, minTextSizeHeight);
732     }
733     if (textStyle.GetMaxLines() == UINT32_MAX) {
734         double baselineOffset = 0.0;
735         textStyle.GetBaselineOffset().NormalizeToPx(pipeline->GetDipScale(), pipeline->GetFontScale(),
736             pipeline->GetLogicScale(), contentConstraint.maxSize.Height(), baselineOffset);
737         double lineHeight = minTextSizeHeight;
738         if (textStyle.HasHeightOverride()) {
739             textStyle.GetLineHeight().NormalizeToPx(pipeline->GetDipScale(), pipeline->GetFontScale(),
740                 pipeline->GetLogicScale(), minTextSizeHeight, lineHeight);
741         }
742         uint32_t maxLines = (contentConstraint.maxSize.Height() - baselineOffset - minTextSizeHeight) / (lineHeight);
743         textStyle.SetMaxLines(maxLines);
744         textStyle.DisableAdaptTextSize();
745 
746         if (!BuildParagraph(textStyle, layoutProperty, contentConstraint, pipeline, layoutWrapper)) {
747             return false;
748         }
749     }
750     // Reducing the value of MaxLines to make sure the parapraph could be layout in the constraint of height.
751     height = static_cast<float>(paragraph_->GetHeight());
752     while (GreatNotEqual(height, contentConstraint.maxSize.Height())) {
753         auto maxLines = textStyle.GetMaxLines();
754         if (maxLines == 0) {
755             break;
756         } else {
757             maxLines = textStyle.GetMaxLines() - 1;
758             textStyle.SetMaxLines(maxLines);
759         }
760         if (!BuildParagraph(textStyle, layoutProperty, contentConstraint, pipeline, layoutWrapper)) {
761             return false;
762         }
763         height = static_cast<float>(paragraph_->GetHeight());
764     }
765     return true;
766 }
767 
BuildTextRaceParagraph(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,const RefPtr<PipelineContext> & pipeline,LayoutWrapper * layoutWrapper)768 std::optional<SizeF> TextLayoutAlgorithm::BuildTextRaceParagraph(TextStyle& textStyle,
769     const RefPtr<TextLayoutProperty>& layoutProperty, const LayoutConstraintF& contentConstraint,
770     const RefPtr<PipelineContext>& pipeline, LayoutWrapper* layoutWrapper)
771 {
772     // create a paragraph with all text in 1 line
773     textStyle.SetTextOverflow(TextOverflow::CLIP);
774     textStyle.SetMaxLines(1);
775     if (!CreateParagraph(textStyle, layoutProperty->GetContent().value_or(""), layoutWrapper)) {
776         return std::nullopt;
777     }
778     if (!paragraph_) {
779         return std::nullopt;
780     }
781 
782     // layout the paragraph to the width of text
783     paragraph_->Layout(std::numeric_limits<float>::max());
784     float paragraphWidth = GetTextWidth();
785     if (contentConstraint.selfIdealSize.Width().has_value()) {
786         paragraphWidth = std::max(contentConstraint.selfIdealSize.Width().value(), paragraphWidth);
787     } else {
788         paragraphWidth = std::max(contentConstraint.maxSize.Width(), paragraphWidth);
789     }
790     paragraph_->Layout(std::ceil(paragraphWidth));
791 
792     textStyle_ = textStyle;
793 
794     // calculate the content size
795     auto height = static_cast<float>(paragraph_->GetHeight());
796     double baselineOffset = 0.0;
797     if (layoutProperty->GetBaselineOffsetValue(Dimension())
798             .NormalizeToPx(
799                 pipeline->GetDipScale(), pipeline->GetFontScale(), pipeline->GetLogicScale(), height, baselineOffset)) {
800         baselineOffset_ = static_cast<float>(baselineOffset);
801     }
802     float heightFinal =
803         std::min(static_cast<float>(height + std::fabs(baselineOffset)), contentConstraint.maxSize.Height());
804 
805     float widthFinal = paragraphWidth;
806     if (contentConstraint.selfIdealSize.Width().has_value()) {
807         if (contentConstraint.selfIdealSize.Width().value() < paragraphWidth) {
808             widthFinal = contentConstraint.selfIdealSize.Width().value();
809         }
810     } else if (contentConstraint.maxSize.Width() < paragraphWidth) {
811         widthFinal = contentConstraint.maxSize.Width();
812     }
813     return SizeF(widthFinal, heightFinal);
814 }
815 
SetPropertyToModifier(const RefPtr<TextLayoutProperty> & layoutProperty,RefPtr<TextContentModifier> modifier)816 void TextLayoutAlgorithm::SetPropertyToModifier(
817     const RefPtr<TextLayoutProperty>& layoutProperty, RefPtr<TextContentModifier> modifier)
818 {
819     auto fontFamily = layoutProperty->GetFontFamily();
820     if (fontFamily.has_value()) {
821         modifier->SetFontFamilies(fontFamily.value());
822     }
823     auto fontSize = layoutProperty->GetFontSize();
824     if (fontSize.has_value()) {
825         modifier->SetFontSize(fontSize.value());
826     }
827     auto fontWeight = layoutProperty->GetFontWeight();
828     if (fontWeight.has_value()) {
829         modifier->SetFontWeight(fontWeight.value());
830     }
831     auto textColor = layoutProperty->GetTextColor();
832     if (textColor.has_value()) {
833         modifier->SetTextColor(textColor.value());
834     }
835     auto textShadow = layoutProperty->GetTextShadow();
836     if (textShadow.has_value()) {
837         modifier->SetTextShadow(textShadow.value());
838     }
839     auto textDecorationColor = layoutProperty->GetTextDecorationColor();
840     if (textDecorationColor.has_value()) {
841         modifier->SetTextDecorationColor(textDecorationColor.value());
842     }
843     auto textDecorationStyle = layoutProperty->GetTextDecorationStyle();
844     if (textDecorationStyle.has_value()) {
845         modifier->SetTextDecorationStyle(textDecorationStyle.value());
846     }
847     auto textDecoration = layoutProperty->GetTextDecoration();
848     if (textDecoration.has_value()) {
849         modifier->SetTextDecoration(textDecoration.value());
850     }
851     auto baselineOffset = layoutProperty->GetBaselineOffset();
852     if (baselineOffset.has_value()) {
853         modifier->SetBaselineOffset(baselineOffset.value());
854     }
855 }
856 
AdaptMaxTextSize(TextStyle & textStyle,const std::string & content,const LayoutConstraintF & contentConstraint,const RefPtr<PipelineContext> & pipeline,LayoutWrapper * layoutWrapper)857 bool TextLayoutAlgorithm::AdaptMaxTextSize(TextStyle& textStyle, const std::string& content,
858     const LayoutConstraintF& contentConstraint, const RefPtr<PipelineContext>& pipeline, LayoutWrapper* layoutWrapper)
859 {
860     double maxFontSize = 0.0;
861     double minFontSize = 0.0;
862     if (!textStyle.GetAdaptMaxFontSize().NormalizeToPx(pipeline->GetDipScale(), pipeline->GetFontScale(),
863             pipeline->GetLogicScale(), contentConstraint.maxSize.Height(), maxFontSize)) {
864         return false;
865     }
866     if (!textStyle.GetAdaptMinFontSize().NormalizeToPx(pipeline->GetDipScale(), pipeline->GetFontScale(),
867             pipeline->GetLogicScale(), contentConstraint.maxSize.Height(), minFontSize)) {
868         return false;
869     }
870     if (LessNotEqual(maxFontSize, minFontSize) || LessOrEqual(minFontSize, 0.0)) {
871         // minFontSize or maxFontSize is invalid
872         if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) {
873             TAG_LOGE(AceLogTag::ACE_TEXT, "create paragraph error");
874             return false;
875         }
876         return true;
877     }
878     constexpr Dimension ADAPT_UNIT = 1.0_fp;
879 
880     auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
881     CHECK_NULL_RETURN(textLayoutProperty, false);
882     auto step = textLayoutProperty->GetAdaptFontSizeStepValue(ADAPT_UNIT);
883     if (GreatNotEqual(textStyle.GetAdaptFontSizeStep().Value(), 0.0)) {
884         step = textStyle.GetAdaptFontSizeStep();
885     }
886     double stepSize = 0.0;
887     if (!step.NormalizeToPx(pipeline->GetDipScale(), pipeline->GetFontScale(), pipeline->GetLogicScale(),
888             contentConstraint.maxSize.Height(), stepSize)) {
889         return false;
890     }
891     auto maxSize = GetMaxMeasureSize(contentConstraint);
892     // Use the minFontSize to layout the paragraph. While using the minFontSize, if the paragraph could be layout in 1
893     // line, then increase the font size and try to layout using the maximum available fontsize.
894     textStyle.SetFontSize(Dimension(minFontSize));
895     if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) {
896         TAG_LOGE(AceLogTag::ACE_TEXT, "create paragraph error");
897         return false;
898     }
899     if (paragraph_->GetLineCount() > 1 || paragraph_->DidExceedMaxLines() ||
900         GreatNotEqual(paragraph_->GetLongestLine(), maxSize.Width())) {
901         return true;
902     }
903     auto tag = static_cast<int32_t>((maxFontSize - minFontSize) / stepSize);
904     auto length = tag + 1 + (GreatNotEqual(maxFontSize, minFontSize + stepSize * tag) ? 1 : 0);
905     int32_t left = 0;
906     int32_t right = length - 1;
907     float fontSize = 0.0f;
908     while (left <= right) {
909         int32_t mid = left + (right - left) / 2;
910         if (mid == length - 1) {
911             fontSize = static_cast<float>(maxFontSize);
912         } else {
913             fontSize = static_cast<float>(minFontSize + stepSize * mid);
914         }
915         textStyle.SetFontSize(Dimension(fontSize));
916         if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) {
917             TAG_LOGE(AceLogTag::ACE_TEXT, "create paragraph error");
918             return false;
919         }
920         if (paragraph_->GetLineCount() <= 1 && !paragraph_->DidExceedMaxLines() &&
921             LessNotEqual(paragraph_->GetLongestLine(), maxSize.Width())) {
922             left = mid + 1;
923         } else {
924             right = mid - 1;
925         }
926     }
927     if (left - 1 == length - 1) {
928         fontSize = static_cast<float>(maxFontSize);
929     } else {
930         fontSize = static_cast<float>(minFontSize + stepSize * (left - 1));
931     }
932     textStyle.SetFontSize(Dimension(fontSize));
933 
934     TAG_LOGD(AceLogTag::ACE_TEXT,
935         "MIN_FONT_SIZE_FIRST, adapt maxTextSize, length: %{public}d result: %{public}d fontSize: %{public}f ", length,
936         left - 1, fontSize);
937 
938     if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) {
939         TAG_LOGE(AceLogTag::ACE_TEXT, "create paragraph error");
940         return false;
941     }
942     return true;
943 }
944 
GetTextStyle() const945 std::optional<TextStyle> TextLayoutAlgorithm::GetTextStyle() const
946 {
947     return textStyle_;
948 }
949 
UpdateTextColorIfForeground(const RefPtr<FrameNode> & frameNode,TextStyle & textStyle)950 void TextLayoutAlgorithm::UpdateTextColorIfForeground(const RefPtr<FrameNode>& frameNode, TextStyle& textStyle)
951 {
952     auto renderContext = frameNode->GetRenderContext();
953     if (renderContext->HasForegroundColor()) {
954         if (renderContext->GetForegroundColorValue().GetValue() != textStyle.GetTextColor().GetValue()) {
955             textStyle.SetTextColor(Color::FOREGROUND);
956         }
957     } else if (renderContext->HasForegroundColorStrategy()) {
958         textStyle.SetTextColor(Color::FOREGROUND);
959     }
960 }
961 
ApplyIndent(const TextStyle & textStyle,double width)962 void TextLayoutAlgorithm::ApplyIndent(const TextStyle& textStyle, double width)
963 {
964     if (LessOrEqual(textStyle.GetTextIndent().Value(), 0.0)) {
965         return;
966     }
967     // first line indent
968     CHECK_NULL_VOID(paragraph_);
969     auto pipeline = PipelineContext::GetCurrentContext();
970     CHECK_NULL_VOID(pipeline);
971     double indent = 0.0;
972     if (textStyle.GetTextIndent().Unit() != DimensionUnit::PERCENT) {
973         if (!textStyle.GetTextIndent().NormalizeToPx(
974                 pipeline->GetDipScale(), pipeline->GetFontScale(), pipeline->GetLogicScale(), width, indent)) {
975             return;
976         }
977     } else {
978         indent = width * textStyle.GetTextIndent().Value();
979     }
980     indent_ = static_cast<float>(indent);
981     std::vector<float> indents;
982     // only indent first line
983     indents.emplace_back(indent_);
984     indents.emplace_back(0.0);
985     paragraph_->SetIndents(indents);
986 }
987 
IncludeImageSpan(LayoutWrapper * layoutWrapper)988 bool TextLayoutAlgorithm::IncludeImageSpan(LayoutWrapper* layoutWrapper)
989 {
990     CHECK_NULL_RETURN(layoutWrapper, false);
991     return (!layoutWrapper->GetAllChildrenWithBuild().empty());
992 }
993 
GetSpanAndImageSpanList(std::list<RefPtr<SpanItem>> & spanList,std::map<int32_t,std::pair<RectF,RefPtr<PlaceholderSpanItem>>> & placeholderSpanList)994 void TextLayoutAlgorithm::GetSpanAndImageSpanList(std::list<RefPtr<SpanItem>>& spanList,
995     std::map<int32_t, std::pair<RectF, RefPtr<PlaceholderSpanItem>>>& placeholderSpanList)
996 {
997     std::vector<RectF> rectsForPlaceholders;
998     paragraph_->GetRectsForPlaceholders(rectsForPlaceholders);
999     for (const auto& child : spanItemChildren_) {
1000         if (!child) {
1001             continue;
1002         }
1003         auto imageSpanItem = AceType::DynamicCast<ImageSpanItem>(child);
1004         if (imageSpanItem) {
1005             int32_t index = child->placeholderIndex;
1006             if (index >= 0 && index < static_cast<int32_t>(rectsForPlaceholders.size())) {
1007                 placeholderSpanList.emplace(index, std::make_pair(rectsForPlaceholders.at(index), imageSpanItem));
1008             }
1009         } else if (auto placeholderSpanItem = AceType::DynamicCast<PlaceholderSpanItem>(child); placeholderSpanItem) {
1010             int32_t index = child->placeholderIndex;
1011             if (index >= 0 && index < static_cast<int32_t>(rectsForPlaceholders.size())) {
1012                 placeholderSpanList.emplace(index, std::make_pair(rectsForPlaceholders.at(index), placeholderSpanItem));
1013             }
1014         } else {
1015             spanList.emplace_back(child);
1016         }
1017     }
1018 }
1019 
SplitSpanContentByLines(const TextStyle & textStyle,const std::list<RefPtr<SpanItem>> & spanList,std::map<int32_t,std::pair<RectF,std::list<RefPtr<SpanItem>>>> & spanContentLines)1020 void TextLayoutAlgorithm::SplitSpanContentByLines(const TextStyle& textStyle,
1021     const std::list<RefPtr<SpanItem>>& spanList,
1022     std::map<int32_t, std::pair<RectF, std::list<RefPtr<SpanItem>>>>& spanContentLines)
1023 {
1024     int32_t currentLine = 0;
1025     int32_t start = GetFirstSpanStartPositon();
1026     for (const auto& child : spanList) {
1027         if (!child) {
1028             continue;
1029         }
1030         std::string textValue = child->content;
1031         std::vector<RectF> selectedRects;
1032         if (!textValue.empty()) {
1033             paragraph_->GetRectsForRange(child->position - StringUtils::ToWstring(textValue).length() - start,
1034                 child->position - start, selectedRects);
1035         }
1036         RectF currentRect = RectF(0, -1, 0, 0);
1037         auto preLinetLastSpan = spanContentLines.rbegin();
1038         double preLineFontSize = textStyle.GetFontSize().Value();
1039         if (preLinetLastSpan != spanContentLines.rend()) {
1040             currentRect = preLinetLastSpan->second.first;
1041             if (preLinetLastSpan->second.second.back() && preLinetLastSpan->second.second.back()->fontStyle &&
1042                 preLinetLastSpan->second.second.back()->fontStyle->GetFontSize().has_value()) {
1043                 preLineFontSize = preLinetLastSpan->second.second.back()->fontStyle->GetFontSize().value().Value();
1044             }
1045         }
1046         for (const auto& rect : selectedRects) {
1047             if (!NearEqual(currentRect.GetOffset().GetY(), rect.GetOffset().GetY())) {
1048                 currentRect = rect;
1049                 ++currentLine;
1050             }
1051 
1052             auto iter = spanContentLines.find(currentLine);
1053             if (iter != spanContentLines.end()) {
1054                 auto iterSecond = std::find(iter->second.second.begin(), iter->second.second.end(), child);
1055                 if (iterSecond != iter->second.second.end()) {
1056                     continue;
1057                 }
1058                 if (NearEqual(rect.GetOffset().GetY(), currentRect.GetOffset().GetY()) && child->fontStyle &&
1059                     child->fontStyle->GetFontSize().has_value() &&
1060                     NearEqual(preLineFontSize, child->fontStyle->GetFontSize().value().Value())) {
1061                     continue;
1062                 }
1063                 iter->second.second.emplace_back(child);
1064             } else {
1065                 std::list<RefPtr<SpanItem>> spanLineList;
1066                 spanLineList.emplace_back(child);
1067                 spanContentLines.emplace(currentLine, std::make_pair(currentRect, spanLineList));
1068             }
1069         }
1070     }
1071 }
1072 
GetFirstSpanStartPositon()1073 int32_t TextLayoutAlgorithm::GetFirstSpanStartPositon()
1074 {
1075     int32_t start = 0;
1076     if (!spanItemChildren_.empty()) {
1077         auto firstSpan = spanItemChildren_.front();
1078         if (firstSpan) {
1079             start = firstSpan->position - static_cast<int32_t>(StringUtils::ToWstring(firstSpan->content).length());
1080         }
1081     }
1082     return start;
1083 }
1084 
SetImageSpanTextStyleByLines(const TextStyle & textStyle,std::map<int32_t,std::pair<RectF,RefPtr<PlaceholderSpanItem>>> & placeholderSpanList,std::map<int32_t,std::pair<RectF,std::list<RefPtr<SpanItem>>>> & spanContentLines)1085 void TextLayoutAlgorithm::SetImageSpanTextStyleByLines(const TextStyle& textStyle,
1086     std::map<int32_t, std::pair<RectF, RefPtr<PlaceholderSpanItem>>>& placeholderSpanList,
1087     std::map<int32_t, std::pair<RectF, std::list<RefPtr<SpanItem>>>>& spanContentLines)
1088 {
1089     auto pipelineContext = PipelineContext::GetCurrentContext();
1090     CHECK_NULL_VOID(pipelineContext);
1091 
1092     auto placeholderItem = placeholderSpanList.begin();
1093     for (auto spanItem = spanContentLines.begin(); spanItem != spanContentLines.end(); spanItem++) {
1094         for (; placeholderItem != placeholderSpanList.end();) {
1095             auto placeholder = placeholderItem->second.second;
1096             if (!placeholder) {
1097                 continue;
1098             }
1099             auto offset = placeholderItem->second.first.GetOffset();
1100             auto spanItemRect = spanItem->second.first;
1101             if (GreatOrEqual(offset.GetY(), spanItemRect.Bottom())) {
1102                 break;
1103             }
1104             auto placeholderItemRect = placeholderItem->second.first;
1105             placeholderItemRect.SetOffset(OffsetF(spanItemRect.GetOffset().GetX(), offset.GetY()));
1106             bool isIntersectWith = spanItem->second.first.IsIntersectWith(placeholderItemRect);
1107             if (!isIntersectWith) {
1108                 placeholderItem++;
1109                 continue;
1110             }
1111             Dimension maxFontSize;
1112             TextStyle spanTextStyle = textStyle;
1113             for (const auto& child : spanItem->second.second) {
1114                 if (!child || !child->fontStyle) {
1115                     continue;
1116                 }
1117                 if (!child->fontStyle->GetFontSize().has_value()) {
1118                     continue;
1119                 }
1120                 if (LessNotEqual(maxFontSize.Value(), child->fontStyle->GetFontSize().value().Value())) {
1121                     maxFontSize = child->fontStyle->GetFontSize().value();
1122                     spanTextStyle = CreateTextStyleUsingTheme(
1123                         child->fontStyle, child->textLineStyle, pipelineContext->GetTheme<TextTheme>());
1124                 }
1125             }
1126             placeholder->textStyle = spanTextStyle;
1127             placeholderItem++;
1128         }
1129     }
1130 }
1131 
SetImageSpanTextStyle(const TextStyle & textStyle)1132 void TextLayoutAlgorithm::SetImageSpanTextStyle(const TextStyle& textStyle)
1133 {
1134     CHECK_NULL_VOID(paragraph_);
1135 
1136     std::list<RefPtr<SpanItem>> spanList;
1137     std::map<int32_t, std::pair<RectF, RefPtr<PlaceholderSpanItem>>> placeholderList;
1138     GetSpanAndImageSpanList(spanList, placeholderList);
1139 
1140     // split text content by lines
1141     std::map<int32_t, std::pair<RectF, std::list<RefPtr<SpanItem>>>> spanContentLines;
1142     SplitSpanContentByLines(textStyle, spanList, spanContentLines);
1143 
1144     // set imagespan textstyle
1145     SetImageSpanTextStyleByLines(textStyle, placeholderList, spanContentLines);
1146 }
1147 
CreateImageSpanAndLayout(const TextStyle & textStyle,const std::string & content,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)1148 bool TextLayoutAlgorithm::CreateImageSpanAndLayout(const TextStyle& textStyle, const std::string& content,
1149     const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
1150 {
1151     bool includeImageSpan = IncludeImageSpan(layoutWrapper);
1152     if (includeImageSpan) {
1153         SetImageSpanTextStyle(textStyle);
1154         if (!CreateParagraph(textStyle, content, layoutWrapper)) {
1155             return false;
1156         }
1157         CHECK_NULL_RETURN(paragraph_, false);
1158         auto maxSize = GetMaxMeasureSize(contentConstraint);
1159         ApplyIndent(textStyle, maxSize.Width());
1160         paragraph_->Layout(maxSize.Width());
1161     }
1162     return true;
1163 }
1164 
GetLineCount() const1165 size_t TextLayoutAlgorithm::GetLineCount() const
1166 {
1167     CHECK_NULL_RETURN(paragraph_, 0);
1168     return paragraph_->GetLineCount();
1169 }
1170 
GetPlaceholderRects(std::vector<RectF> & rects)1171 void TextLayoutAlgorithm::GetPlaceholderRects(std::vector<RectF>& rects)
1172 {
1173     CHECK_NULL_VOID(paragraph_);
1174     paragraph_->GetRectsForPlaceholders(rects);
1175 }
1176 
GetParagraphStyle(const TextStyle & textStyle,const std::string & content,LayoutWrapper * layoutWrapper) const1177 ParagraphStyle TextLayoutAlgorithm::GetParagraphStyle(
1178     const TextStyle& textStyle, const std::string& content, LayoutWrapper* layoutWrapper) const
1179 {
1180     return {
1181         .direction = GetTextDirection(content, layoutWrapper),
1182         .align = textStyle.GetTextAlign(),
1183         .maxLines = textStyle.GetMaxLines(),
1184         .fontLocale = Localization::GetInstance()->GetFontLocale(),
1185         .wordBreak = textStyle.GetWordBreak(),
1186         .ellipsisMode = textStyle.GetEllipsisMode(),
1187         .textOverflow = textStyle.GetTextOverflow(),
1188     };
1189 }
1190 } // namespace OHOS::Ace::NG
1191