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