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