• 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/components/text/text_theme.h"
26 #include "core/components_ng/base/frame_node.h"
27 #include "core/components_ng/pattern/image/image_layout_property.h"
28 #include "core/components_ng/pattern/text/text_layout_property.h"
29 #include "core/components_ng/pattern/text/text_pattern.h"
30 #include "core/components_ng/render/drawing_prop_convertor.h"
31 #include "core/components_ng/render/font_collection.h"
32 #include "core/pipeline_ng/pipeline_context.h"
33 #include "core/common/font_manager.h"
34 
35 namespace OHOS::Ace::NG {
36 namespace {
37 /**
38  * The baseline information needs to be calculated based on contentOffsetY.
39  */
GetContentOffsetY(LayoutWrapper * layoutWrapper)40 float GetContentOffsetY(LayoutWrapper* layoutWrapper)
41 {
42     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
43     const auto& padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
44     auto offsetY = padding.top.value_or(0);
45     auto align = Alignment::CENTER;
46     if (layoutWrapper->GetLayoutProperty()->GetPositionProperty()) {
47         align = layoutWrapper->GetLayoutProperty()->GetPositionProperty()->GetAlignment().value_or(align);
48     }
49     const auto& content = layoutWrapper->GetGeometryNode()->GetContent();
50     if (content) {
51         offsetY += Alignment::GetAlignPosition(size, content->GetRect().GetSize(), align).GetY();
52     }
53     return offsetY;
54 }
55 } // namespace
56 
57 TextLayoutAlgorithm::TextLayoutAlgorithm() = default;
58 
OnReset()59 void TextLayoutAlgorithm::OnReset() {}
60 
MeasureContent(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)61 std::optional<SizeF> TextLayoutAlgorithm::MeasureContent(
62     const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
63 {
64     if (!contentConstraint.maxSize.IsPositive()) {
65         return std::nullopt;
66     }
67 
68     auto frameNode = layoutWrapper->GetHostNode();
69     CHECK_NULL_RETURN(frameNode, std::nullopt);
70     auto pipeline = frameNode->GetContext();
71     CHECK_NULL_RETURN(pipeline, std::nullopt);
72     auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
73     CHECK_NULL_RETURN(textLayoutProperty, std::nullopt);
74     auto pattern = frameNode->GetPattern<TextPattern>();
75     CHECK_NULL_RETURN(pattern, std::nullopt);
76     auto contentModifier = pattern->GetContentModifier();
77 
78     TextStyle textStyle = CreateTextStyleUsingTheme(
79         textLayoutProperty->GetFontStyle(), textLayoutProperty->GetTextLineStyle(), pipeline->GetTheme<TextTheme>());
80     if (contentModifier) {
81         SetPropertyToModifier(textLayoutProperty, contentModifier);
82         contentModifier->ModifyTextStyle(textStyle);
83         contentModifier->SetFontReady(false);
84     }
85     // Register callback for fonts.
86     FontRegisterCallback(frameNode, textStyle);
87 
88     // Determines whether a foreground color is set or inherited.
89     UpdateTextColorIfForeground(frameNode, textStyle);
90 
91     if (textStyle.GetTextOverflow() == TextOverflow::MARQUEE) {
92         return BuildTextRaceParagraph(textStyle, textLayoutProperty, contentConstraint, pipeline, layoutWrapper);
93     }
94 
95     if (!AddPropertiesAndAnimations(textStyle, textLayoutProperty, contentConstraint, pipeline, layoutWrapper)) {
96         return std::nullopt;
97     }
98 
99     textStyle_ = textStyle;
100 
101     auto height = static_cast<float>(paragraph_->GetHeight());
102     double baselineOffset = 0.0;
103     textStyle.GetBaselineOffset().NormalizeToPx(
104         pipeline->GetDipScale(), pipeline->GetFontScale(), pipeline->GetLogicScale(), 0.0f, baselineOffset);
105 
106     baselineOffset_ = static_cast<float>(baselineOffset);
107 
108     auto heightFinal = static_cast<float>(height + std::fabs(baselineOffset));
109     if (contentConstraint.selfIdealSize.Height().has_value()) {
110         heightFinal = std::min(
111             static_cast<float>(height + std::fabs(baselineOffset)), contentConstraint.selfIdealSize.Height().value());
112     } else {
113         heightFinal =
114             std::min(static_cast<float>(height + std::fabs(baselineOffset)), contentConstraint.maxSize.Height());
115     }
116     return SizeF(paragraph_->GetMaxWidth(), heightFinal);
117 }
118 
AddPropertiesAndAnimations(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & textLayoutProperty,const LayoutConstraintF & contentConstraint,const RefPtr<PipelineContext> & pipeline,LayoutWrapper * layoutWrapper)119 bool TextLayoutAlgorithm::AddPropertiesAndAnimations(TextStyle& textStyle,
120     const RefPtr<TextLayoutProperty>& textLayoutProperty, const LayoutConstraintF& contentConstraint,
121     const RefPtr<PipelineContext>& pipeline, LayoutWrapper* layoutWrapper)
122 {
123     bool result = false;
124     switch (textLayoutProperty->GetHeightAdaptivePolicyValue(TextHeightAdaptivePolicy::MAX_LINES_FIRST)) {
125         case TextHeightAdaptivePolicy::MAX_LINES_FIRST:
126             result = BuildParagraph(textStyle, textLayoutProperty, contentConstraint, pipeline, layoutWrapper);
127             break;
128         case TextHeightAdaptivePolicy::MIN_FONT_SIZE_FIRST:
129             result = BuildParagraphAdaptUseMinFontSize(
130                 textStyle, textLayoutProperty, contentConstraint, pipeline, layoutWrapper);
131             break;
132         case TextHeightAdaptivePolicy::LAYOUT_CONSTRAINT_FIRST:
133             result = BuildParagraphAdaptUseLayoutConstraint(
134                 textStyle, textLayoutProperty, contentConstraint, pipeline, layoutWrapper);
135             break;
136         default:
137             break;
138     }
139     return result;
140 }
141 
FontRegisterCallback(const RefPtr<FrameNode> & frameNode,const TextStyle & textStyle)142 void TextLayoutAlgorithm::FontRegisterCallback(const RefPtr<FrameNode>& frameNode, const TextStyle& textStyle)
143 {
144     auto callback = [weakNode = WeakPtr<FrameNode>(frameNode)] {
145         auto frameNode = weakNode.Upgrade();
146         CHECK_NULL_VOID(frameNode);
147         frameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
148         auto pattern = frameNode->GetPattern<TextPattern>();
149         CHECK_NULL_VOID(pattern);
150         auto modifier = DynamicCast<TextContentModifier>(pattern->GetContentModifier());
151         CHECK_NULL_VOID(modifier);
152         modifier->SetFontReady(true);
153     };
154     auto pipeline = frameNode->GetContext();
155     CHECK_NULL_VOID(pipeline);
156     auto fontManager = pipeline->GetFontManager();
157     if (fontManager) {
158         bool isCustomFont = false;
159         for (const auto& familyName : textStyle.GetFontFamilies()) {
160             bool customFont = fontManager->RegisterCallbackNG(frameNode, familyName, callback);
161             if (customFont) {
162                 isCustomFont = true;
163             }
164         }
165         fontManager->AddVariationNodeNG(frameNode);
166         if (isCustomFont) {
167             auto pattern = frameNode->GetPattern<TextPattern>();
168             CHECK_NULL_VOID(pattern);
169             pattern->SetIsCustomFont(true);
170             auto modifier = DynamicCast<TextContentModifier>(pattern->GetContentModifier());
171             CHECK_NULL_VOID(modifier);
172             modifier->SetIsCustomFont(true);
173         }
174     }
175 }
176 
Measure(LayoutWrapper * layoutWrapper)177 void TextLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
178 {
179     BoxLayoutAlgorithm::Measure(layoutWrapper);
180     auto baselineDistance = 0.0f;
181     if (paragraph_) {
182         baselineDistance = paragraph_->GetAlphabeticBaseline() + std::max(GetBaselineOffset(), 0.0f);
183     }
184     if (!NearZero(baselineDistance, 0.0f)) {
185         baselineDistance += GetContentOffsetY(layoutWrapper);
186     }
187     layoutWrapper->GetGeometryNode()->SetBaselineDistance(baselineDistance);
188 }
189 
UpdateParagraph(LayoutWrapper * layoutWrapper)190 void TextLayoutAlgorithm::UpdateParagraph(LayoutWrapper* layoutWrapper)
191 {
192     int32_t spanTextLength = 0;
193     CHECK_NULL_VOID(layoutWrapper);
194     auto layoutProperty = layoutWrapper->GetLayoutProperty();
195     CHECK_NULL_VOID(layoutProperty);
196     auto frameNode = layoutWrapper->GetHostNode();
197     const auto& layoutConstrain = layoutProperty->CreateChildConstraint();
198     const auto& children = layoutWrapper->GetAllChildrenWithBuild();
199     auto iterItems = children.begin();
200     for (const auto& child : spanItemChildren_) {
201         if (!child) {
202             continue;
203         }
204         auto imageSpanItem = AceType::DynamicCast<ImageSpanItem>(child);
205         if (imageSpanItem) {
206             if (iterItems == children.end() || !(*iterItems)) {
207                 continue;
208             }
209             (*iterItems)->Measure(layoutConstrain);
210             auto verticalAlign = VerticalAlign::BOTTOM;
211             auto imageLayoutProperty = DynamicCast<ImageLayoutProperty>((*iterItems)->GetLayoutProperty());
212             if (imageLayoutProperty) {
213                 verticalAlign = imageLayoutProperty->GetVerticalAlign().value_or(VerticalAlign::BOTTOM);
214             }
215             auto geometryNode = (*iterItems)->GetGeometryNode();
216             if (!geometryNode) {
217                 iterItems++;
218                 continue;
219             }
220             auto width = geometryNode->GetMarginFrameSize().Width();
221             auto height = geometryNode->GetMarginFrameSize().Height();
222             child->placeHolderIndex = child->UpdateParagraph(frameNode, paragraph_, width, height, verticalAlign);
223             child->content = " ";
224             child->position = spanTextLength + 1;
225             spanTextLength += 1;
226             iterItems++;
227         } else {
228             child->UpdateParagraph(frameNode, paragraph_);
229             child->position = spanTextLength + StringUtils::ToWstring(child->content).length();
230             spanTextLength += StringUtils::ToWstring(child->content).length();
231         }
232     }
233 }
234 
CreateParagraph(const TextStyle & textStyle,std::string content,LayoutWrapper * layoutWrapper)235 bool TextLayoutAlgorithm::CreateParagraph(const TextStyle& textStyle, std::string content, LayoutWrapper* layoutWrapper)
236 {
237     ParagraphStyle paraStyle = { .direction = GetTextDirection(content),
238         .align = textStyle.GetTextAlign(),
239         .maxLines = textStyle.GetMaxLines(),
240         .fontLocale = Localization::GetInstance()->GetFontLocale(),
241         .wordBreak = textStyle.GetWordBreak(),
242         .textOverflow = textStyle.GetTextOverflow() };
243     paragraph_ = Paragraph::Create(paraStyle, FontCollection::Current());
244     CHECK_NULL_RETURN(paragraph_, false);
245     paragraph_->PushStyle(textStyle);
246 
247     if (spanItemChildren_.empty()) {
248         StringUtils::TransformStrCase(content, static_cast<int32_t>(textStyle.GetTextCase()));
249         paragraph_->AddText(StringUtils::Str8ToStr16(content));
250     } else {
251         UpdateParagraph(layoutWrapper);
252     }
253     paragraph_->Build();
254     return true;
255 }
256 
CreateParagraphAndLayout(const TextStyle & textStyle,const std::string & content,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)257 bool TextLayoutAlgorithm::CreateParagraphAndLayout(const TextStyle& textStyle, const std::string& content,
258     const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
259 {
260     if (!CreateParagraph(textStyle, content, layoutWrapper)) {
261         return false;
262     }
263     CHECK_NULL_RETURN(paragraph_, false);
264     auto maxSize = GetMaxMeasureSize(contentConstraint);
265     if (GreatNotEqual(textStyle.GetTextIndent().Value(), 0.0)) {
266         ApplyIndents(textStyle, maxSize.Width());
267     }
268     paragraph_->Layout(maxSize.Width());
269 
270     bool ret = CreateImageSpanAndLayout(textStyle, content, contentConstraint, layoutWrapper);
271     return ret;
272 }
273 
GetContentOffset(LayoutWrapper * layoutWrapper) const274 OffsetF TextLayoutAlgorithm::GetContentOffset(LayoutWrapper* layoutWrapper) const
275 {
276     OffsetF contentOffset(0.0, 0.0);
277     CHECK_NULL_RETURN(layoutWrapper, contentOffset);
278 
279     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
280     const auto& padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
281     MinusPaddingToSize(padding, size);
282     auto left = padding.left.value_or(0);
283     auto top = padding.top.value_or(0);
284     auto paddingOffset = OffsetF(left, top);
285     auto align = Alignment::CENTER;
286     if (layoutWrapper->GetLayoutProperty()->GetPositionProperty()) {
287         align = layoutWrapper->GetLayoutProperty()->GetPositionProperty()->GetAlignment().value_or(align);
288     }
289 
290     const auto& content = layoutWrapper->GetGeometryNode()->GetContent();
291     if (content) {
292         contentOffset = Alignment::GetAlignPosition(size, content->GetRect().GetSize(), align) + paddingOffset;
293         content->SetOffset(contentOffset);
294     }
295     return contentOffset;
296 }
297 
Layout(LayoutWrapper * layoutWrapper)298 void TextLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
299 {
300     CHECK_NULL_VOID(layoutWrapper);
301     auto contentOffset = GetContentOffset(layoutWrapper);
302     CHECK_NULL_VOID(paragraph_);
303     std::vector<int32_t> placeHolderIndex;
304     for (const auto& child : spanItemChildren_) {
305         if (!child) {
306             continue;
307         }
308         auto imageSpanItem = AceType::DynamicCast<ImageSpanItem>(child);
309         if (imageSpanItem) {
310             placeHolderIndex.emplace_back(child->placeHolderIndex);
311         }
312     }
313     if (spanItemChildren_.empty() || placeHolderIndex.empty()) {
314         return;
315     }
316 
317     size_t index = 0;
318     size_t imageSpanIndex = 0;
319     std::vector<Rect> rectsForPlaceholders;
320     paragraph_->GetRectsForPlaceholders(rectsForPlaceholders);
321     const auto& children = layoutWrapper->GetAllChildrenWithBuild();
322     for (const auto& child : children) {
323         if (!child) {
324             ++index;
325             continue;
326         }
327         if (index >= placeHolderIndex.size()) {
328             return;
329         }
330         imageSpanIndex = placeHolderIndex.at(index);
331         if (imageSpanIndex >= rectsForPlaceholders.size() || imageSpanIndex < 0) {
332             ++index;
333             continue;
334         }
335         auto rect = rectsForPlaceholders.at(imageSpanIndex);
336         LOGI("ImageSpan Left= %{public}f, Top = %{public}f, width = %{public}f, height = %{public}f", rect.Left(),
337             rect.Top(), rect.Width(), rect.Height());
338         auto geometryNode = child->GetGeometryNode();
339         if (!geometryNode) {
340             ++index;
341             continue;
342         }
343         geometryNode->SetMarginFrameOffset(contentOffset + OffsetF(rect.Left(), rect.Top()));
344         child->Layout();
345         ++index;
346     }
347 
348 #ifdef ENABLE_DRAG_FRAMEWORK
349     auto frameNode = layoutWrapper->GetHostNode();
350     CHECK_NULL_VOID(frameNode);
351     auto pipeline = frameNode->GetContext();
352     CHECK_NULL_VOID(pipeline);
353     auto pattern = frameNode->GetPattern<TextPattern>();
354     CHECK_NULL_VOID(pattern);
355     pattern->InitSpanImageLayout(placeHolderIndex, rectsForPlaceholders, contentOffset);
356 #endif
357 }
358 
AdaptMinTextSize(TextStyle & textStyle,const std::string & content,const LayoutConstraintF & contentConstraint,const RefPtr<PipelineContext> & pipeline,LayoutWrapper * layoutWrapper)359 bool TextLayoutAlgorithm::AdaptMinTextSize(TextStyle& textStyle, const std::string& content,
360     const LayoutConstraintF& contentConstraint, const RefPtr<PipelineContext>& pipeline, LayoutWrapper* layoutWrapper)
361 {
362     double maxFontSize = 0.0;
363     double minFontSize = 0.0;
364     if (!textStyle.GetAdaptMaxFontSize().NormalizeToPx(pipeline->GetDipScale(), pipeline->GetFontScale(),
365             pipeline->GetLogicScale(), contentConstraint.maxSize.Height(), maxFontSize)) {
366         return false;
367     }
368     if (!textStyle.GetAdaptMinFontSize().NormalizeToPx(pipeline->GetDipScale(), pipeline->GetFontScale(),
369             pipeline->GetLogicScale(), contentConstraint.maxSize.Height(), minFontSize)) {
370         return false;
371     }
372     if (LessNotEqual(maxFontSize, minFontSize) || LessOrEqual(minFontSize, 0.0)) {
373         if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) {
374             LOGE("fail to initialize text paragraph when adapt min text size.");
375             return false;
376         }
377         return true;
378     }
379     constexpr Dimension ADAPT_UNIT = 1.0_fp;
380     Dimension step = ADAPT_UNIT;
381     if (GreatNotEqual(textStyle.GetAdaptFontSizeStep().Value(), 0.0)) {
382         step = textStyle.GetAdaptFontSizeStep();
383     }
384     double stepSize = 0.0;
385     if (!step.NormalizeToPx(pipeline->GetDipScale(), pipeline->GetFontScale(), pipeline->GetLogicScale(),
386             contentConstraint.maxSize.Height(), stepSize)) {
387         return false;
388     }
389     auto maxSize = GetMaxMeasureSize(contentConstraint);
390     while (GreatOrEqual(maxFontSize, minFontSize)) {
391         textStyle.SetFontSize(Dimension(maxFontSize));
392         if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) {
393             return false;
394         }
395         if (!DidExceedMaxLines(maxSize)) {
396             break;
397         }
398         maxFontSize -= stepSize;
399     }
400     return true;
401 }
402 
DidExceedMaxLines(const SizeF & maxSize)403 bool TextLayoutAlgorithm::DidExceedMaxLines(const SizeF& maxSize)
404 {
405     CHECK_NULL_RETURN(paragraph_, false);
406     bool didExceedMaxLines = paragraph_->DidExceedMaxLines();
407     didExceedMaxLines = didExceedMaxLines || GreatNotEqual(paragraph_->GetHeight(), maxSize.Height());
408     didExceedMaxLines = didExceedMaxLines || GreatNotEqual(paragraph_->GetLongestLine(), maxSize.Width());
409     return didExceedMaxLines;
410 }
411 
GetTextDirection(const std::string & content)412 TextDirection TextLayoutAlgorithm::GetTextDirection(const std::string& content)
413 {
414     TextDirection textDirection = TextDirection::LTR;
415     auto showingTextForWString = StringUtils::ToWstring(content);
416     for (const auto& charOfShowingText : showingTextForWString) {
417         if (TextLayoutadapter::IsLeftToRight(charOfShowingText)) {
418             return TextDirection::LTR;
419         } else if (TextLayoutadapter::IsRightToLeft(charOfShowingText)) {
420             return TextDirection::RTL;
421         } else if (TextLayoutadapter::IsRightTOLeftArabic(charOfShowingText)) {
422             return TextDirection::RTL;
423         }
424     }
425     return textDirection;
426 }
427 
GetTextWidth() const428 float TextLayoutAlgorithm::GetTextWidth() const
429 {
430     CHECK_NULL_RETURN(paragraph_, 0.0);
431     return paragraph_->GetTextWidth();
432 }
433 
GetParagraph()434 const RefPtr<Paragraph>& TextLayoutAlgorithm::GetParagraph()
435 {
436     return paragraph_;
437 }
438 
GetBaselineOffset() const439 float TextLayoutAlgorithm::GetBaselineOffset() const
440 {
441     return baselineOffset_;
442 }
443 
GetMaxMeasureSize(const LayoutConstraintF & contentConstraint) const444 SizeF TextLayoutAlgorithm::GetMaxMeasureSize(const LayoutConstraintF& contentConstraint) const
445 {
446     auto maxSize = contentConstraint.selfIdealSize;
447     maxSize.UpdateIllegalSizeWithCheck(contentConstraint.maxSize);
448     return maxSize.ConvertToSizeT();
449 }
450 
GetSpanItemChildren()451 std::list<RefPtr<SpanItem>>&& TextLayoutAlgorithm::GetSpanItemChildren()
452 {
453     return std::move(spanItemChildren_);
454 }
455 
BuildParagraph(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,const RefPtr<PipelineContext> & pipeline,LayoutWrapper * layoutWrapper)456 bool TextLayoutAlgorithm::BuildParagraph(TextStyle& textStyle, const RefPtr<TextLayoutProperty>& layoutProperty,
457     const LayoutConstraintF& contentConstraint, const RefPtr<PipelineContext>& pipeline, LayoutWrapper* layoutWrapper)
458 {
459     if (!textStyle.GetAdaptTextSize()) {
460         if (!CreateParagraphAndLayout(
461             textStyle, layoutProperty->GetContent().value_or(""), contentConstraint, layoutWrapper)) {
462             return false;
463         }
464     } else {
465         if (!AdaptMinTextSize(
466             textStyle, layoutProperty->GetContent().value_or(""), contentConstraint, pipeline, layoutWrapper)) {
467             return false;
468         }
469     }
470 
471     // Confirmed specification: The width of the text paragraph covers the width of the component, so this code is
472     // generally not allowed to be modified
473     if (!contentConstraint.selfIdealSize.Width()) {
474         float paragraphNewWidth = std::min(GetTextWidth(), paragraph_->GetMaxWidth());
475         paragraphNewWidth =
476             std::clamp(paragraphNewWidth, contentConstraint.minSize.Width(), contentConstraint.maxSize.Width());
477         if (!NearEqual(paragraphNewWidth, paragraph_->GetMaxWidth())) {
478             paragraph_->Layout(std::ceil(paragraphNewWidth));
479         }
480     }
481     return true;
482 }
483 
BuildParagraphAdaptUseMinFontSize(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,const RefPtr<PipelineContext> & pipeline,LayoutWrapper * layoutWrapper)484 bool TextLayoutAlgorithm::BuildParagraphAdaptUseMinFontSize(TextStyle& textStyle,
485     const RefPtr<TextLayoutProperty>& layoutProperty, const LayoutConstraintF& contentConstraint,
486     const RefPtr<PipelineContext>& pipeline, LayoutWrapper* layoutWrapper)
487 {
488     if (!AdaptMaxTextSize(
489         textStyle, layoutProperty->GetContent().value_or(""), contentConstraint, pipeline, layoutWrapper)) {
490         return false;
491     }
492 
493     // Confirmed specification: The width of the text paragraph covers the width of the component, so this code is
494     // generally not allowed to be modified
495     if (!contentConstraint.selfIdealSize.Width()) {
496         float paragraphNewWidth = std::min(GetTextWidth(), paragraph_->GetMaxWidth());
497         paragraphNewWidth =
498             std::clamp(paragraphNewWidth, contentConstraint.minSize.Width(), contentConstraint.maxSize.Width());
499         if (!NearEqual(paragraphNewWidth, paragraph_->GetMaxWidth())) {
500             paragraph_->Layout(std::ceil(paragraphNewWidth));
501         }
502     }
503 
504     return true;
505 }
506 
BuildParagraphAdaptUseLayoutConstraint(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,const RefPtr<PipelineContext> & pipeline,LayoutWrapper * layoutWrapper)507 bool TextLayoutAlgorithm::BuildParagraphAdaptUseLayoutConstraint(TextStyle& textStyle,
508     const RefPtr<TextLayoutProperty>& layoutProperty, const LayoutConstraintF& contentConstraint,
509     const RefPtr<PipelineContext>& pipeline, LayoutWrapper* layoutWrapper)
510 {
511     // Create the paragraph and obtain the height.
512     if (!BuildParagraph(textStyle, layoutProperty, contentConstraint, pipeline, layoutWrapper)) {
513         return false;
514     }
515     auto height = static_cast<float>(paragraph_->GetHeight());
516     double minTextSizeHeight = 0.0;
517     textStyle.GetAdaptMinFontSize().NormalizeToPx(
518         pipeline->GetDipScale(), pipeline->GetFontScale(), pipeline->GetLogicScale(), height, minTextSizeHeight);
519     if (LessOrEqual(minTextSizeHeight, 0.0)) {
520         textStyle.GetFontSize().NormalizeToPx(
521             pipeline->GetDipScale(), pipeline->GetFontScale(), pipeline->GetLogicScale(), height, minTextSizeHeight);
522     }
523     if (textStyle.GetMaxLines() == UINT32_MAX) {
524         double baselineOffset = 0.0;
525         textStyle.GetBaselineOffset().NormalizeToPx(pipeline->GetDipScale(), pipeline->GetFontScale(),
526             pipeline->GetLogicScale(), contentConstraint.maxSize.Height(), baselineOffset);
527         double lineHeight = minTextSizeHeight;
528         if (textStyle.HasHeightOverride()) {
529             textStyle.GetLineHeight().NormalizeToPx(pipeline->GetDipScale(), pipeline->GetFontScale(),
530                 pipeline->GetLogicScale(), minTextSizeHeight, lineHeight);
531         }
532         uint32_t maxLines = (contentConstraint.maxSize.Height() - baselineOffset - minTextSizeHeight) / (lineHeight);
533         textStyle.SetMaxLines(maxLines);
534         textStyle.DisableAdaptTextSize();
535 
536         if (!BuildParagraph(textStyle, layoutProperty, contentConstraint, pipeline, layoutWrapper)) {
537             return false;
538         }
539     }
540     // Reducing the value of MaxLines to make sure the parapraph could be layout in the constraint of height.
541     height = static_cast<float>(paragraph_->GetHeight());
542     while (GreatNotEqual(height, contentConstraint.maxSize.Height())) {
543         auto maxLines = textStyle.GetMaxLines();
544         if (maxLines == 0) {
545             break;
546         } else {
547             maxLines = textStyle.GetMaxLines() - 1;
548             textStyle.SetMaxLines(maxLines);
549         }
550         if (!BuildParagraph(textStyle, layoutProperty, contentConstraint, pipeline, layoutWrapper)) {
551             return false;
552         }
553         height = static_cast<float>(paragraph_->GetHeight());
554     }
555     return true;
556 }
557 
BuildTextRaceParagraph(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,const RefPtr<PipelineContext> & pipeline,LayoutWrapper * layoutWrapper)558 std::optional<SizeF> TextLayoutAlgorithm::BuildTextRaceParagraph(TextStyle& textStyle,
559     const RefPtr<TextLayoutProperty>& layoutProperty, const LayoutConstraintF& contentConstraint,
560     const RefPtr<PipelineContext>& pipeline, LayoutWrapper* layoutWrapper)
561 {
562     // create a paragraph with all text in 1 line
563     textStyle.SetTextOverflow(TextOverflow::CLIP);
564     textStyle.SetMaxLines(1);
565     textStyle.SetTextAlign(TextAlign::START);
566     if (!CreateParagraph(textStyle, layoutProperty->GetContent().value_or(""), layoutWrapper)) {
567         return std::nullopt;
568     }
569     if (!paragraph_) {
570         return std::nullopt;
571     }
572 
573     // layout the paragraph to the width of text
574     paragraph_->Layout(std::numeric_limits<float>::max());
575     float paragraphWidth = paragraph_->GetMaxWidth();
576     paragraph_->Layout(paragraphWidth);
577 
578     textStyle_ = textStyle;
579 
580     // calculate the content size
581     auto height = static_cast<float>(paragraph_->GetHeight());
582     double baselineOffset = 0.0;
583     if (layoutProperty->GetBaselineOffsetValue(Dimension())
584             .NormalizeToPx(
585                 pipeline->GetDipScale(), pipeline->GetFontScale(), pipeline->GetLogicScale(), height, baselineOffset)) {
586         baselineOffset_ = static_cast<float>(baselineOffset);
587     }
588     float heightFinal =
589         std::min(static_cast<float>(height + std::fabs(baselineOffset)), contentConstraint.maxSize.Height());
590 
591     float widthFinal = paragraphWidth;
592     if (contentConstraint.selfIdealSize.Width().has_value()) {
593         if (contentConstraint.selfIdealSize.Width().value() < paragraphWidth) {
594             widthFinal = contentConstraint.selfIdealSize.Width().value();
595         }
596     } else if (contentConstraint.maxSize.Width() < paragraphWidth) {
597         widthFinal = contentConstraint.maxSize.Width();
598     }
599     return SizeF(widthFinal, heightFinal);
600 }
601 
SetPropertyToModifier(const RefPtr<TextLayoutProperty> & layoutProperty,RefPtr<TextContentModifier> modifier)602 void TextLayoutAlgorithm::SetPropertyToModifier(
603     const RefPtr<TextLayoutProperty>& layoutProperty, RefPtr<TextContentModifier> modifier)
604 {
605     auto fontFamily = layoutProperty->GetFontFamily();
606     if (fontFamily.has_value()) {
607         modifier->SetFontFamilies(fontFamily.value());
608     }
609     auto fontSize = layoutProperty->GetFontSize();
610     if (fontSize.has_value()) {
611         modifier->SetFontSize(fontSize.value());
612     }
613     auto fontWeight = layoutProperty->GetFontWeight();
614     if (fontWeight.has_value()) {
615         modifier->SetFontWeight(fontWeight.value());
616     }
617     auto textColor = layoutProperty->GetTextColor();
618     if (textColor.has_value()) {
619         modifier->SetTextColor(textColor.value());
620     }
621     auto textShadow = layoutProperty->GetTextShadow();
622     if (textShadow.has_value()) {
623         modifier->SetTextShadow(textShadow.value());
624     }
625     auto textDecorationColor = layoutProperty->GetTextDecorationColor();
626     if (textDecorationColor.has_value()) {
627         modifier->SetTextDecorationColor(textDecorationColor.value());
628     }
629     auto textDecoration = layoutProperty->GetTextDecoration();
630     if (textDecoration.has_value()) {
631         modifier->SetTextDecoration(textDecoration.value());
632     }
633     auto baselineOffset = layoutProperty->GetBaselineOffset();
634     if (baselineOffset.has_value()) {
635         modifier->SetBaselineOffset(baselineOffset.value());
636     }
637 }
638 
AdaptMaxTextSize(TextStyle & textStyle,const std::string & content,const LayoutConstraintF & contentConstraint,const RefPtr<PipelineContext> & pipeline,LayoutWrapper * layoutWrapper)639 bool TextLayoutAlgorithm::AdaptMaxTextSize(TextStyle& textStyle, const std::string& content,
640     const LayoutConstraintF& contentConstraint, const RefPtr<PipelineContext>& pipeline, LayoutWrapper* layoutWrapper)
641 {
642     double maxFontSize = 0.0;
643     double minFontSize = 0.0;
644     if (!textStyle.GetAdaptMaxFontSize().NormalizeToPx(pipeline->GetDipScale(), pipeline->GetFontScale(),
645             pipeline->GetLogicScale(), contentConstraint.maxSize.Height(), maxFontSize)) {
646         return false;
647     }
648     if (!textStyle.GetAdaptMinFontSize().NormalizeToPx(pipeline->GetDipScale(), pipeline->GetFontScale(),
649             pipeline->GetLogicScale(), contentConstraint.maxSize.Height(), minFontSize)) {
650         return false;
651     }
652     if (LessNotEqual(maxFontSize, minFontSize) || LessOrEqual(minFontSize, 0.0)) {
653         // minFontSize or maxFontSize is invalid
654         if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) {
655             LOGE("fail to initialize text paragraph when adapt min text size.");
656             return false;
657         }
658         return true;
659     }
660     constexpr Dimension ADAPT_UNIT = 1.0_fp;
661     Dimension step = ADAPT_UNIT;
662     if (GreatNotEqual(textStyle.GetAdaptFontSizeStep().Value(), 0.0)) {
663         step = textStyle.GetAdaptFontSizeStep();
664     }
665     double stepSize = 0.0;
666     if (!step.NormalizeToPx(pipeline->GetDipScale(), pipeline->GetFontScale(), pipeline->GetLogicScale(),
667             contentConstraint.maxSize.Height(), stepSize)) {
668         return false;
669     }
670     RefPtr<Paragraph> paragraph;
671     auto maxSize = GetMaxMeasureSize(contentConstraint);
672     // Use the minFontSize to layout the paragraph. While using the minFontSize, if the paragraph could be layout in 1
673     // line, then increase the font size and try to layout using the maximum available fontsize.
674     while (LessOrEqual(minFontSize, maxFontSize)) {
675         textStyle.SetFontSize(Dimension(minFontSize));
676         if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) {
677             return false;
678         }
679         if (paragraph_->GetLineCount() > 1 || paragraph_->DidExceedMaxLines() ||
680             GreatNotEqual(paragraph_->GetLongestLine(), maxSize.Width())) {
681             if (paragraph != nullptr) {
682                 paragraph_ = paragraph;
683             }
684             break;
685         }
686         minFontSize += stepSize;
687         paragraph = paragraph_;
688     }
689     return true;
690 }
691 
GetTextStyle() const692 std::optional<TextStyle> TextLayoutAlgorithm::GetTextStyle() const
693 {
694     return textStyle_;
695 }
696 
UpdateTextColorIfForeground(const RefPtr<FrameNode> & frameNode,TextStyle & textStyle)697 void TextLayoutAlgorithm::UpdateTextColorIfForeground(const RefPtr<FrameNode>& frameNode, TextStyle& textStyle)
698 {
699     auto renderContext = frameNode->GetRenderContext();
700     if (renderContext->HasForegroundColor()) {
701         if (renderContext->GetForegroundColorValue().GetValue() != textStyle.GetTextColor().GetValue()) {
702             textStyle.SetTextColor(Color::FOREGROUND);
703         }
704     } else if (renderContext->HasForegroundColorStrategy()) {
705         textStyle.SetTextColor(Color::FOREGROUND);
706     }
707 }
708 
ApplyIndents(const TextStyle & textStyle,double width)709 void TextLayoutAlgorithm::ApplyIndents(const TextStyle& textStyle, double width)
710 {
711     CHECK_NULL_VOID(paragraph_);
712     auto pipeline = PipelineContext::GetCurrentContext();
713     CHECK_NULL_VOID(pipeline);
714     double indent = 0.0;
715     if (textStyle.GetTextIndent().Unit() != DimensionUnit::PERCENT) {
716         if (!textStyle.GetTextIndent().NormalizeToPx(
717             pipeline->GetDipScale(), pipeline->GetFontScale(), pipeline->GetLogicScale(), width, indent)) {
718             return;
719         }
720     } else {
721         indent = width * textStyle.GetTextIndent().Value();
722     }
723     std::vector<float> indents;
724     indents.emplace_back(static_cast<float>(indent));
725     indents.emplace_back(0.0);
726     paragraph_->SetIndents(indents);
727 }
728 
IncludeImageSpan(LayoutWrapper * layoutWrapper)729 bool TextLayoutAlgorithm::IncludeImageSpan(LayoutWrapper* layoutWrapper)
730 {
731     CHECK_NULL_RETURN(layoutWrapper, false);
732     return (!layoutWrapper->GetAllChildrenWithBuild().empty());
733 }
734 
GetSpanAndImageSpanList(std::list<RefPtr<SpanItem>> & spanList,std::map<int32_t,std::pair<Rect,RefPtr<ImageSpanItem>>> & imageSpanList)735 void TextLayoutAlgorithm::GetSpanAndImageSpanList(
736     std::list<RefPtr<SpanItem>>& spanList, std::map<int32_t, std::pair<Rect, RefPtr<ImageSpanItem>>>& imageSpanList)
737 {
738     std::vector<Rect> rectsForPlaceholders;
739     paragraph_->GetRectsForPlaceholders(rectsForPlaceholders);
740 
741     for (const auto& child : spanItemChildren_) {
742         if (!child) {
743             continue;
744         }
745         auto imageSpanItem = AceType::DynamicCast<ImageSpanItem>(child);
746         if (imageSpanItem) {
747             int32_t index = child->placeHolderIndex;
748             if (index >= 0 && index < static_cast<int32_t>(rectsForPlaceholders.size())) {
749                 imageSpanList.emplace(index, std::make_pair(rectsForPlaceholders.at(index), imageSpanItem));
750             }
751         } else {
752             spanList.emplace_back(child);
753         }
754     }
755 }
756 
SplitSpanContentByLines(const TextStyle & textStyle,const std::list<RefPtr<SpanItem>> & spanList,std::map<int32_t,std::pair<Rect,std::list<RefPtr<SpanItem>>>> & spanContentLines)757 void TextLayoutAlgorithm::SplitSpanContentByLines(const TextStyle& textStyle,
758     const std::list<RefPtr<SpanItem>>& spanList,
759     std::map<int32_t, std::pair<Rect, std::list<RefPtr<SpanItem>>>>& spanContentLines)
760 {
761     int32_t currentLine = 0;
762     size_t currentLength = 0;
763     for (const auto& child : spanList) {
764         if (!child) {
765             continue;
766         }
767         std::string textValue = child->content;
768         std::vector<Rect> selectedRects;
769         if (!textValue.empty()) {
770             paragraph_->GetRectsForRange(currentLength, currentLength + textValue.size(), selectedRects);
771         }
772         currentLength += textValue.size();
773         Rect currentRect;
774         auto preLinetLastSpan = spanContentLines.rbegin();
775         double preLineFontSize = textStyle.GetFontSize().Value();
776         if (preLinetLastSpan != spanContentLines.rend()) {
777             currentRect = preLinetLastSpan->second.first;
778             if (preLinetLastSpan->second.second.back() && preLinetLastSpan->second.second.back()->fontStyle) {
779                 preLineFontSize = preLinetLastSpan->second.second.back()->fontStyle->GetFontSize().value().Value();
780             }
781         }
782         for (const auto& rect : selectedRects) {
783             if (!NearEqual(currentRect.GetOffset().GetY(), rect.GetOffset().GetY())) {
784                 currentRect = rect;
785                 ++currentLine;
786             }
787 
788             auto iter = spanContentLines.find(currentLine);
789             if (iter != spanContentLines.end()) {
790                 auto iterSecond = std::find(iter->second.second.begin(), iter->second.second.end(), child);
791                 if (iterSecond != iter->second.second.end()) {
792                     continue;
793                 }
794                 if (NearEqual(rect.GetOffset().GetY(), currentRect.GetOffset().GetY()) && child->fontStyle &&
795                     NearEqual(preLineFontSize, child->fontStyle->GetFontSize().value().Value())) {
796                     continue;
797                 }
798                 iter->second.second.emplace_back(child);
799             } else {
800                 std::list<RefPtr<SpanItem>> spanLineList;
801                 spanLineList.emplace_back(child);
802                 spanContentLines.emplace(currentLine, std::make_pair(currentRect, spanLineList));
803             }
804         }
805     }
806 }
807 
SetImageSpanTextStyleByLines(const TextStyle & textStyle,std::map<int32_t,std::pair<Rect,RefPtr<ImageSpanItem>>> & imageSpanList,std::map<int32_t,std::pair<Rect,std::list<RefPtr<SpanItem>>>> & spanContentLines)808 void TextLayoutAlgorithm::SetImageSpanTextStyleByLines(const TextStyle& textStyle,
809     std::map<int32_t, std::pair<Rect, RefPtr<ImageSpanItem>>>& imageSpanList,
810     std::map<int32_t, std::pair<Rect, std::list<RefPtr<SpanItem>>>>& spanContentLines)
811 {
812     auto pipelineContext = PipelineContext::GetCurrentContext();
813     CHECK_NULL_VOID(pipelineContext);
814 
815     auto imageSpanItem = imageSpanList.begin();
816     for (auto spanItem = spanContentLines.begin(); spanItem != spanContentLines.end(); spanItem++) {
817         for (; imageSpanItem != imageSpanList.end();) {
818             auto imageSpan = imageSpanItem->second.second;
819             if (!imageSpan) {
820                 continue;
821             }
822             imageSpan->textStyle = textStyle;
823 
824             auto offset = imageSpanItem->second.first.GetOffset();
825             auto imageSpanItemRect = imageSpanItem->second.first;
826             imageSpanItemRect.SetOffset(Offset(spanItem->second.first.GetOffset().GetX(), offset.GetY()));
827             bool isIntersectWith = spanItem->second.first.IsIntersectWith(imageSpanItemRect);
828             if (!isIntersectWith) {
829                 break;
830             }
831             Dimension maxFontSize;
832             TextStyle spanTextStyle = textStyle;
833             for (const auto& child : spanItem->second.second) {
834                 if (!child || !child->fontStyle) {
835                     continue;
836                 }
837                 if (!child->fontStyle->GetFontSize().has_value()) {
838                     continue;
839                 }
840                 if (LessNotEqual(maxFontSize.Value(), child->fontStyle->GetFontSize().value().Value())) {
841                     maxFontSize = child->fontStyle->GetFontSize().value();
842                     spanTextStyle = CreateTextStyleUsingTheme(
843                         child->fontStyle, child->textLineStyle, pipelineContext->GetTheme<TextTheme>());
844                 }
845             }
846             imageSpan->textStyle = spanTextStyle;
847             imageSpanItem++;
848         }
849     }
850 }
851 
SetImageSpanTextStyle(const TextStyle & textStyle)852 void TextLayoutAlgorithm::SetImageSpanTextStyle(const TextStyle& textStyle)
853 {
854     CHECK_NULL_VOID(paragraph_);
855 
856     std::list<RefPtr<SpanItem>> spanList;
857     std::map<int32_t, std::pair<Rect, RefPtr<ImageSpanItem>>> imageSpanList;
858     GetSpanAndImageSpanList(spanList, imageSpanList);
859 
860     // split text content by lines
861     std::map<int32_t, std::pair<Rect, std::list<RefPtr<SpanItem>>>> spanContentLines;
862     SplitSpanContentByLines(textStyle, spanList, spanContentLines);
863 
864     // set imagespan textstyle
865     SetImageSpanTextStyleByLines(textStyle, imageSpanList, spanContentLines);
866 }
867 
CreateImageSpanAndLayout(const TextStyle & textStyle,const std::string & content,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)868 bool TextLayoutAlgorithm::CreateImageSpanAndLayout(const TextStyle& textStyle, const std::string& content,
869     const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
870 {
871     bool includeImageSpan = IncludeImageSpan(layoutWrapper);
872     if (includeImageSpan) {
873         SetImageSpanTextStyle(textStyle);
874         if (!CreateParagraph(textStyle, content, layoutWrapper)) {
875             return false;
876         }
877         CHECK_NULL_RETURN(paragraph_, false);
878         auto maxSize = GetMaxMeasureSize(contentConstraint);
879         if (GreatNotEqual(textStyle.GetTextIndent().Value(), 0.0)) {
880             ApplyIndents(textStyle, maxSize.Width());
881         }
882         paragraph_->Layout(maxSize.Width());
883     }
884     return true;
885 }
886 
GetLineCount() const887 size_t TextLayoutAlgorithm::GetLineCount() const
888 {
889     CHECK_NULL_RETURN(paragraph_, 0);
890     return paragraph_->GetLineCount();
891 }
892 } // namespace OHOS::Ace::NG
893