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 auto customSpanItem = AceType::DynamicCast<CustomSpanItem>(child);
166 if (customSpanItem && !customSpanItem->isFrameNode) {
167 continue;
168 }
169 if (AceType::InstanceOf<ImageSpanItem>(child) || AceType::InstanceOf<PlaceholderSpanItem>(child)) {
170 placeholderIndex.emplace_back(child->placeholderIndex);
171 }
172 }
173 }
174 }
175
GetSpanParagraphStyle(LayoutWrapper * layoutWrapper,const RefPtr<SpanItem> & spanItem,ParagraphStyle & pStyle)176 void MultipleParagraphLayoutAlgorithm::GetSpanParagraphStyle(
177 LayoutWrapper* layoutWrapper, const RefPtr<SpanItem>& spanItem, ParagraphStyle& pStyle)
178 {
179 const auto& lineStyle = spanItem->textLineStyle;
180 CHECK_NULL_VOID(lineStyle);
181 if (lineStyle->HasTextAlign()) {
182 pStyle.align = lineStyle->GetTextAlignValue();
183 }
184 if (lineStyle->HasMaxLines()) {
185 pStyle.maxLines = lineStyle->GetMaxLinesValue();
186 }
187 if (lineStyle->HasWordBreak()) {
188 pStyle.wordBreak = lineStyle->GetWordBreakValue();
189 }
190 if (lineStyle->HasEllipsisMode()) {
191 pStyle.ellipsisMode = lineStyle->GetEllipsisModeValue();
192 }
193 if (lineStyle->HasTextOverflow()) {
194 pStyle.textOverflow = lineStyle->GetTextOverflowValue();
195 }
196 if (lineStyle->HasTextIndent()) {
197 pStyle.indent = lineStyle->GetTextIndentValue();
198 }
199 if (lineStyle->HasLineBreakStrategy()) {
200 pStyle.lineBreakStrategy = lineStyle->GetLineBreakStrategyValue();
201 }
202 if (lineStyle->HasLeadingMargin()) {
203 pStyle.leadingMargin = lineStyle->GetLeadingMarginValue();
204 }
205 if (lineStyle->HasLineHeight()) {
206 pStyle.lineHeight = lineStyle->GetLineHeightValue();
207 }
208 if (layoutWrapper) {
209 pStyle.direction = GetTextDirection(spanItem->content, layoutWrapper);
210 } else {
211 pStyle.direction = GetTextDirectionByContent(spanItem->content);
212 }
213 }
214
FontRegisterCallback(const RefPtr<FrameNode> & frameNode,const TextStyle & textStyle)215 void MultipleParagraphLayoutAlgorithm::FontRegisterCallback(
216 const RefPtr<FrameNode>& frameNode, const TextStyle& textStyle)
217 {
218 auto callback = [weakNode = WeakPtr<FrameNode>(frameNode)] {
219 auto frameNode = weakNode.Upgrade();
220 CHECK_NULL_VOID(frameNode);
221 frameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
222 auto pattern = frameNode->GetPattern<TextPattern>();
223 CHECK_NULL_VOID(pattern);
224 auto modifier = DynamicCast<TextContentModifier>(pattern->GetContentModifier());
225 CHECK_NULL_VOID(modifier);
226 modifier->SetFontReady(true);
227 };
228 auto pipeline = frameNode->GetContext();
229 CHECK_NULL_VOID(pipeline);
230 auto fontManager = pipeline->GetFontManager();
231 if (fontManager) {
232 bool isCustomFont = false;
233 for (const auto& familyName : textStyle.GetFontFamilies()) {
234 bool customFont = fontManager->RegisterCallbackNG(frameNode, familyName, callback);
235 if (customFont) {
236 isCustomFont = true;
237 }
238 }
239 if (isCustomFont || fontManager->IsDefaultFontChanged()) {
240 auto pattern = frameNode->GetPattern<TextPattern>();
241 CHECK_NULL_VOID(pattern);
242 pattern->SetIsCustomFont(true);
243 auto modifier = DynamicCast<TextContentModifier>(pattern->GetContentModifier());
244 CHECK_NULL_VOID(modifier);
245 modifier->SetIsCustomFont(true);
246 }
247 }
248 }
249
UpdateTextColorIfForeground(const RefPtr<FrameNode> & frameNode,TextStyle & textStyle)250 void MultipleParagraphLayoutAlgorithm::UpdateTextColorIfForeground(
251 const RefPtr<FrameNode>& frameNode, TextStyle& textStyle)
252 {
253 auto renderContext = frameNode->GetRenderContext();
254 if (renderContext->HasForegroundColor()) {
255 if (renderContext->GetForegroundColorValue().GetValue() != textStyle.GetTextColor().GetValue()) {
256 textStyle.SetTextColor(Color::FOREGROUND);
257 }
258 } else if (renderContext->HasForegroundColorStrategy()) {
259 textStyle.SetTextColor(Color::FOREGROUND);
260 }
261 }
262
SetFontSizePropertyToModifier(const RefPtr<TextLayoutProperty> & layoutProperty,const RefPtr<TextContentModifier> & modifier,const TextStyle & textStyle)263 void MultipleParagraphLayoutAlgorithm::SetFontSizePropertyToModifier(const RefPtr<TextLayoutProperty>& layoutProperty,
264 const RefPtr<TextContentModifier>& modifier, const TextStyle& textStyle)
265 {
266 auto fontSize = layoutProperty->GetFontSize();
267 if (fontSize.has_value()) {
268 modifier->SetFontSize(fontSize.value(), textStyle);
269 } else {
270 // Reset modifier FontSize.
271 modifier->SetFontSize(textStyle.GetFontSize(), textStyle, true);
272 }
273 auto adaptMinFontSize = layoutProperty->GetAdaptMinFontSize();
274 if (adaptMinFontSize.has_value()) {
275 modifier->SetAdaptMinFontSize(adaptMinFontSize.value(), textStyle);
276 } else {
277 // Reset modifier MinFontSize.
278 modifier->SetAdaptMinFontSize(textStyle.GetAdaptMinFontSize(), textStyle, true);
279 }
280 auto adaptMaxFontSize = layoutProperty->GetAdaptMaxFontSize();
281 if (adaptMaxFontSize.has_value()) {
282 modifier->SetAdaptMaxFontSize(adaptMaxFontSize.value(), textStyle);
283 } else {
284 // Reset modifier MaxFontSize.
285 modifier->SetAdaptMaxFontSize(textStyle.GetAdaptMaxFontSize(), textStyle, true);
286 }
287 }
288
SetDecorationPropertyToModifier(const RefPtr<TextLayoutProperty> & layoutProperty,const RefPtr<TextContentModifier> & modifier,const TextStyle & textStyle)289 void MultipleParagraphLayoutAlgorithm::SetDecorationPropertyToModifier(const RefPtr<TextLayoutProperty>& layoutProperty,
290 const RefPtr<TextContentModifier>& modifier, const TextStyle& textStyle)
291 {
292 auto textDecorationColor = layoutProperty->GetTextDecorationColor();
293 if (textDecorationColor.has_value()) {
294 modifier->SetTextDecorationColor(textDecorationColor.value());
295 } else {
296 modifier->SetTextDecorationColor(textStyle.GetTextDecorationColor(), true);
297 }
298 auto textDecoration = layoutProperty->GetTextDecoration();
299 if (textDecoration.has_value()) {
300 modifier->SetTextDecoration(textDecoration.value());
301 } else {
302 modifier->SetTextDecoration(textStyle.GetTextDecoration(), true);
303 }
304 }
305
SetPropertyToModifier(const RefPtr<TextLayoutProperty> & layoutProperty,const RefPtr<TextContentModifier> & modifier,const TextStyle & textStyle)306 void MultipleParagraphLayoutAlgorithm::SetPropertyToModifier(const RefPtr<TextLayoutProperty>& layoutProperty,
307 const RefPtr<TextContentModifier>& modifier, const TextStyle& textStyle)
308 {
309 SetFontSizePropertyToModifier(layoutProperty, modifier, textStyle);
310 auto fontWeight = layoutProperty->GetFontWeight();
311 if (fontWeight.has_value()) {
312 modifier->SetFontWeight(fontWeight.value());
313 } else {
314 modifier->SetFontWeight(textStyle.GetFontWeight(), true);
315 }
316 auto textColor = layoutProperty->GetTextColor();
317 if (textColor.has_value()) {
318 modifier->SetTextColor(textColor.value());
319 } else {
320 modifier->SetTextColor(textStyle.GetTextColor(), true);
321 }
322 auto textShadow = layoutProperty->GetTextShadow();
323 if (textShadow.has_value()) {
324 modifier->SetTextShadow(textShadow.value());
325 } else {
326 modifier->SetTextShadow(textStyle.GetTextShadows());
327 }
328 SetDecorationPropertyToModifier(layoutProperty, modifier, textStyle);
329 auto baselineOffset = layoutProperty->GetBaselineOffset();
330 if (baselineOffset.has_value()) {
331 modifier->SetBaselineOffset(baselineOffset.value(), textStyle);
332 } else {
333 modifier->SetBaselineOffset(textStyle.GetBaselineOffset(), textStyle, true);
334 }
335 auto lineHeight = layoutProperty->GetLineHeight();
336 if (lineHeight.has_value()) {
337 if (lineHeight->Unit() == DimensionUnit::PERCENT) {
338 modifier->SetLineHeight(lineHeight.value(), textStyle, true);
339 } else {
340 modifier->SetLineHeight(lineHeight.value(), textStyle);
341 }
342 } else {
343 modifier->SetLineHeight(textStyle.GetLineHeight(), textStyle, true);
344 }
345 }
346
GetSingleParagraph() const347 RefPtr<Paragraph> MultipleParagraphLayoutAlgorithm::GetSingleParagraph() const
348 {
349 CHECK_NULL_RETURN(paragraphManager_, nullptr);
350 CHECK_NULL_RETURN(!paragraphManager_->GetParagraphs().empty(), nullptr);
351 auto paragraphInfo = paragraphManager_->GetParagraphs().front();
352 auto paragraph = paragraphInfo.paragraph;
353 CHECK_NULL_RETURN(paragraph, nullptr);
354 return paragraph;
355 }
356
SetContentOffset(LayoutWrapper * layoutWrapper)357 OffsetF MultipleParagraphLayoutAlgorithm::SetContentOffset(LayoutWrapper* layoutWrapper)
358 {
359 OffsetF contentOffset(0.0f, 0.0f);
360 CHECK_NULL_RETURN(layoutWrapper, contentOffset);
361
362 auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
363 const auto& padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
364 MinusPaddingToSize(padding, size);
365 auto left = padding.left.value_or(0);
366 auto top = padding.top.value_or(0);
367 auto paddingOffset = OffsetF(left, top);
368 auto align = Alignment::CENTER;
369 if (layoutWrapper->GetLayoutProperty()->GetPositionProperty()) {
370 align = layoutWrapper->GetLayoutProperty()->GetPositionProperty()->GetAlignment().value_or(align);
371 }
372
373 const auto& content = layoutWrapper->GetGeometryNode()->GetContent();
374 if (content) {
375 contentOffset = Alignment::GetAlignPosition(size, content->GetRect().GetSize(), align) + paddingOffset;
376 content->SetOffset(contentOffset);
377 }
378 return contentOffset;
379 }
380
GetParagraphStyle(const TextStyle & textStyle,const std::string & content,LayoutWrapper * layoutWrapper) const381 ParagraphStyle MultipleParagraphLayoutAlgorithm::GetParagraphStyle(
382 const TextStyle& textStyle, const std::string& content, LayoutWrapper* layoutWrapper) const
383 {
384 return { .direction = GetTextDirection(content, layoutWrapper),
385 .align = textStyle.GetTextAlign(),
386 .maxLines = static_cast<int32_t>(textStyle.GetMaxLines()) < 0 ? UINT32_MAX : textStyle.GetMaxLines(),
387 .fontLocale = Localization::GetInstance()->GetFontLocale(),
388 .wordBreak = textStyle.GetWordBreak(),
389 .ellipsisMode = textStyle.GetEllipsisMode(),
390 .lineBreakStrategy = textStyle.GetLineBreakStrategy(),
391 .textOverflow = textStyle.GetTextOverflow(),
392 .indent = textStyle.GetTextIndent()
393 };
394 }
395
GetTextDirection(const std::string & content,LayoutWrapper * layoutWrapper)396 TextDirection MultipleParagraphLayoutAlgorithm::GetTextDirection(
397 const std::string& content, LayoutWrapper* layoutWrapper)
398 {
399 auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
400 CHECK_NULL_RETURN(textLayoutProperty, TextDirection::LTR);
401
402 auto direction = textLayoutProperty->GetLayoutDirection();
403 if (direction == TextDirection::LTR || direction == TextDirection::RTL) {
404 return direction;
405 }
406
407 return GetTextDirectionByContent(content);
408 }
409
GetTextDirectionByContent(const std::string & content)410 TextDirection MultipleParagraphLayoutAlgorithm::GetTextDirectionByContent(const std::string& content)
411 {
412 bool isRTL = AceApplicationInfo::GetInstance().IsRightToLeft();
413 auto textDirection = isRTL ? TextDirection::RTL : TextDirection::LTR;
414 auto showingTextForWString = StringUtils::ToWstring(content);
415 for (const auto& charOfShowingText : showingTextForWString) {
416 if (TextLayoutadapter::IsLeftToRight(charOfShowingText)) {
417 return TextDirection::LTR;
418 } else if (TextLayoutadapter::IsRightToLeft(charOfShowingText)) {
419 return TextDirection::RTL;
420 } else if (TextLayoutadapter::IsRightTOLeftArabic(charOfShowingText)) {
421 return TextDirection::RTL;
422 }
423 }
424 return textDirection;
425 }
426
ParagraphReLayout(const LayoutConstraintF & contentConstraint)427 bool MultipleParagraphLayoutAlgorithm::ParagraphReLayout(const LayoutConstraintF& contentConstraint)
428 {
429 ACE_TEXT_SCOPED_TRACE("ParagraphReLayout");
430 // Confirmed specification: The width of the text paragraph covers the width of the component, so this code is
431 // generally not allowed to be modified
432 CHECK_NULL_RETURN(paragraphManager_, false);
433 auto paragraphs = paragraphManager_->GetParagraphs();
434 float paragraphNewWidth =
435 std::min(std::min(paragraphManager_->GetTextWidthIncludeIndent(), paragraphManager_->GetMaxWidth()),
436 GetMaxMeasureSize(contentConstraint).Width());
437 paragraphNewWidth =
438 std::clamp(paragraphNewWidth, contentConstraint.minSize.Width(), contentConstraint.maxSize.Width());
439 if (!contentConstraint.selfIdealSize.Width()) {
440 for (auto pIter = paragraphs.begin(); pIter != paragraphs.end(); pIter++) {
441 auto paragraph = pIter->paragraph;
442 CHECK_NULL_RETURN(paragraph, false);
443 if (!NearEqual(paragraphNewWidth, paragraph->GetMaxWidth())) {
444 OTHER_DURATION();
445 paragraph->Layout(std::ceil(paragraphNewWidth));
446 }
447 }
448 }
449 return true;
450 }
451
UpdateParagraphBySpan(LayoutWrapper * layoutWrapper,ParagraphStyle paraStyle,double maxWidth,const TextStyle & textStyle)452 bool MultipleParagraphLayoutAlgorithm::UpdateParagraphBySpan(LayoutWrapper* layoutWrapper,
453 ParagraphStyle paraStyle, double maxWidth, const TextStyle& textStyle)
454 {
455 CHECK_NULL_RETURN(layoutWrapper, false);
456 auto layoutProperty = layoutWrapper->GetLayoutProperty();
457 CHECK_NULL_RETURN(layoutProperty, false);
458 auto frameNode = layoutWrapper->GetHostNode();
459 CHECK_NULL_RETURN(frameNode, false);
460 const auto& layoutConstrain = layoutProperty->CreateChildConstraint();
461 auto placeHolderLayoutConstrain = layoutConstrain;
462 placeHolderLayoutConstrain.maxSize.SetHeight(Infinity<float>());
463 placeHolderLayoutConstrain.percentReference.SetHeight(0);
464 const auto& children = layoutWrapper->GetAllChildrenWithBuild();
465 auto iterItems = children.begin();
466 auto pattern = frameNode->GetPattern<TextPattern>();
467 CHECK_NULL_RETURN(pattern, false);
468 auto aiSpanMap = pattern->GetAISpanMap();
469 int32_t spanTextLength = 0;
470 std::vector<WeakPtr<FrameNode>> imageNodeList;
471 std::vector<CustomSpanPlaceholderInfo> customSpanPlaceholderInfo;
472 int32_t paragraphIndex = -1;
473 preParagraphsPlaceholderCount_ = 0;
474 currentParagraphPlaceholderCount_ = 0;
475 paragraphFontSize_ = paraStyle.fontSize;
476 auto maxLines = static_cast<int32_t>(paraStyle.maxLines);
477 for (auto&& group : spans_) {
478 ParagraphStyle spanParagraphStyle = paraStyle;
479 RefPtr<SpanItem> paraStyleSpanItem = GetParagraphStyleSpanItem(group);
480 if (paraStyleSpanItem) {
481 GetSpanParagraphStyle(layoutWrapper, paraStyleSpanItem, spanParagraphStyle);
482 if (paraStyleSpanItem->fontStyle->HasFontSize()) {
483 spanParagraphStyle.fontSize = paraStyleSpanItem->fontStyle->GetFontSizeValue().ConvertToPxDistribute(
484 textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
485 }
486 }
487 if (paraStyle.maxLines != UINT32_MAX && !spanStringHasMaxLines_ && isSpanStringMode_) {
488 if (!paragraphManager_->GetParagraphs().empty()) {
489 maxLines -= static_cast<int32_t>(paragraphManager_->GetParagraphs().back().paragraph->GetLineCount());
490 }
491 spanParagraphStyle.maxLines = std::max(maxLines, 0);
492 }
493 auto&& paragraph = Paragraph::Create(spanParagraphStyle, FontCollection::Current());
494 CHECK_NULL_RETURN(paragraph, false);
495 auto paraStart = spanTextLength;
496 paragraphIndex++;
497 for (const auto& child : group) {
498 if (!child) {
499 continue;
500 }
501 child->paragraphIndex = paragraphIndex;
502 child->SetTextPattern(pattern);
503 auto imageSpanItem = AceType::DynamicCast<ImageSpanItem>(child);
504 if (imageSpanItem) {
505 if (iterItems == children.end() || !(*iterItems)) {
506 continue;
507 }
508 AddImageToParagraph(imageSpanItem, (*iterItems), layoutConstrain, paragraph, spanTextLength, textStyle);
509 auto imageNode = (*iterItems)->GetHostNode();
510 imageNodeList.emplace_back(WeakClaim(RawPtr(imageNode)));
511 iterItems++;
512 } else if (AceType::DynamicCast<CustomSpanItem>(child)) {
513 CustomSpanPlaceholderInfo customSpanPlaceholder;
514 customSpanPlaceholder.paragraphIndex = paragraphIndex;
515 auto customSpanItem = AceType::DynamicCast<CustomSpanItem>(child);
516 UpdateParagraphByCustomSpan(
517 customSpanItem, layoutWrapper, paragraph, spanTextLength, customSpanPlaceholder);
518 customSpanPlaceholderInfo.emplace_back(customSpanPlaceholder);
519 if (customSpanItem->isFrameNode) {
520 iterItems++; // CAPI custom span is frameNode,need to move the iterator backwards
521 }
522 } else if (AceType::InstanceOf<PlaceholderSpanItem>(child)) {
523 if (iterItems == children.end() || !(*iterItems)) {
524 continue;
525 }
526 auto placeholderSpanItem = AceType::DynamicCast<PlaceholderSpanItem>(child);
527 if (!placeholderSpanItem) {
528 continue;
529 }
530 AddPlaceHolderToParagraph(
531 placeholderSpanItem, (*iterItems), placeHolderLayoutConstrain, paragraph, spanTextLength);
532 iterItems++;
533 } else if (child->unicode != 0) {
534 AddSymbolSpanToParagraph(child, spanTextLength, frameNode, paragraph);
535 } else {
536 child->aiSpanMap = aiSpanMap;
537 AddTextSpanToParagraph(child, spanTextLength, frameNode, paragraph);
538 aiSpanMap = child->aiSpanMap;
539 }
540 }
541 preParagraphsPlaceholderCount_ += currentParagraphPlaceholderCount_;
542 currentParagraphPlaceholderCount_ = 0;
543 shadowOffset_ += GetShadowOffset(group);
544 HandleEmptyParagraph(paragraph, group);
545 paragraph->Build();
546 ApplyIndent(spanParagraphStyle, paragraph, maxWidth, textStyle);
547 UpdateSymbolSpanEffect(frameNode, paragraph, group);
548 if (paraStyle.maxLines != UINT32_MAX && !spanStringHasMaxLines_ && isSpanStringMode_) {
549 paragraph->Layout(static_cast<float>(maxWidth));
550 }
551 paragraphManager_->AddParagraph({ .paragraph = paragraph,
552 .paragraphStyle = spanParagraphStyle,
553 .start = paraStart,
554 .end = spanTextLength });
555 }
556 pattern->SetImageSpanNodeList(imageNodeList);
557 pattern->InitCustomSpanPlaceholderInfo(customSpanPlaceholderInfo);
558 return true;
559 }
560
AddSymbolSpanToParagraph(const RefPtr<SpanItem> & child,int32_t & spanTextLength,const RefPtr<FrameNode> & frameNode,const RefPtr<Paragraph> & paragraph)561 void MultipleParagraphLayoutAlgorithm::AddSymbolSpanToParagraph(const RefPtr<SpanItem>& child, int32_t& spanTextLength,
562 const RefPtr<FrameNode>& frameNode, const RefPtr<Paragraph>& paragraph)
563 {
564 child->SetIsParentText(frameNode->GetTag() == V2::TEXT_ETS_TAG);
565 child->UpdateSymbolSpanParagraph(frameNode, paragraph);
566 spanTextLength += SYMBOL_SPAN_LENGTH;
567 child->length = SYMBOL_SPAN_LENGTH;
568 child->position = spanTextLength;
569 child->content = " ";
570 }
571
AddTextSpanToParagraph(const RefPtr<SpanItem> & child,int32_t & spanTextLength,const RefPtr<FrameNode> & frameNode,const RefPtr<Paragraph> & paragraph)572 void MultipleParagraphLayoutAlgorithm::AddTextSpanToParagraph(const RefPtr<SpanItem>& child, int32_t& spanTextLength,
573 const RefPtr<FrameNode>& frameNode, const RefPtr<Paragraph>& paragraph)
574 {
575 child->length = StringUtils::ToWstring(child->content).length();
576 spanTextLength += static_cast<int32_t>(child->length);
577 child->position = spanTextLength;
578 child->UpdateParagraph(frameNode, paragraph, isSpanStringMode_, PlaceholderStyle(), isMarquee_);
579 }
580
AddImageToParagraph(RefPtr<ImageSpanItem> & imageSpanItem,const RefPtr<LayoutWrapper> & layoutWrapper,const LayoutConstraintF & layoutConstrain,const RefPtr<Paragraph> & paragraph,int32_t & spanTextLength,const TextStyle & textStyle)581 void MultipleParagraphLayoutAlgorithm::AddImageToParagraph(RefPtr<ImageSpanItem>& imageSpanItem,
582 const RefPtr<LayoutWrapper>& layoutWrapper, const LayoutConstraintF& layoutConstrain,
583 const RefPtr<Paragraph>& paragraph, int32_t& spanTextLength, const TextStyle& textStyle)
584 {
585 auto frameNode = layoutWrapper->GetHostNode();
586 CHECK_NULL_VOID(frameNode);
587 auto id = frameNode->GetId();
588 int32_t targetId = imageSpanItem->imageNodeId;
589 if (!isSpanStringMode_) {
590 CHECK_NULL_VOID(id == targetId);
591 }
592 layoutWrapper->Measure(layoutConstrain);
593 PlaceholderStyle placeholderStyle;
594 auto baselineOffset = Dimension(0.0f);
595 auto imageLayoutProperty = DynamicCast<ImageLayoutProperty>(layoutWrapper->GetLayoutProperty());
596 if (imageLayoutProperty) {
597 placeholderStyle.verticalAlign = imageLayoutProperty->GetVerticalAlign().value_or(VerticalAlign::BOTTOM);
598 baselineOffset = imageLayoutProperty->GetBaselineOffset().value_or(Dimension(0.0f));
599 }
600 auto geometryNode = layoutWrapper->GetGeometryNode();
601 CHECK_NULL_VOID(geometryNode);
602 placeholderStyle.width = geometryNode->GetMarginFrameSize().Width();
603 placeholderStyle.height = geometryNode->GetMarginFrameSize().Height();
604 placeholderStyle.paragraphFontSize = Dimension(paragraphFontSize_);
605 placeholderStyle.paragraphTextColor = textStyle_.value_or(TextStyle()).GetTextColor();
606 if (NearZero(baselineOffset.Value())) {
607 imageSpanItem->placeholderIndex =
608 imageSpanItem->UpdateParagraph(frameNode, paragraph, isSpanStringMode_, placeholderStyle);
609 } else {
610 placeholderStyle.baselineOffset = baselineOffset.ConvertToPxDistribute(
611 textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
612 imageSpanItem->placeholderIndex =
613 imageSpanItem->UpdateParagraph(frameNode, paragraph, isSpanStringMode_, placeholderStyle);
614 }
615 currentParagraphPlaceholderCount_++;
616 imageSpanItem->placeholderIndex += preParagraphsPlaceholderCount_;
617 imageSpanItem->content = " ";
618 spanTextLength += 1;
619 imageSpanItem->position = spanTextLength;
620 imageSpanItem->length = 1;
621 }
622
AddPlaceHolderToParagraph(RefPtr<PlaceholderSpanItem> & placeholderSpanItem,const RefPtr<LayoutWrapper> & layoutWrapper,const LayoutConstraintF & layoutConstrain,const RefPtr<Paragraph> & paragraph,int32_t & spanTextLength)623 void MultipleParagraphLayoutAlgorithm::AddPlaceHolderToParagraph(RefPtr<PlaceholderSpanItem>& placeholderSpanItem,
624 const RefPtr<LayoutWrapper>& layoutWrapper, const LayoutConstraintF& layoutConstrain,
625 const RefPtr<Paragraph>& paragraph, int32_t& spanTextLength)
626 {
627 auto frameNode = layoutWrapper->GetHostNode();
628 CHECK_NULL_VOID(frameNode);
629 auto id = frameNode->GetId();
630 int32_t targetId = placeholderSpanItem->placeholderSpanNodeId;
631 CHECK_NULL_VOID(id == targetId);
632 // find the Corresponding ImageNode for every ImageSpanItem
633 layoutWrapper->Measure(layoutConstrain);
634 auto geometryNode = layoutWrapper->GetGeometryNode();
635 CHECK_NULL_VOID(geometryNode);
636 PlaceholderStyle placeholderStyle;
637 placeholderStyle.width = geometryNode->GetMarginFrameSize().Width();
638 placeholderStyle.height = geometryNode->GetMarginFrameSize().Height();
639 placeholderStyle.verticalAlign = VerticalAlign::NONE;
640 placeholderSpanItem->placeholderIndex =
641 placeholderSpanItem->UpdateParagraph(frameNode, paragraph, isSpanStringMode_, placeholderStyle);
642 currentParagraphPlaceholderCount_++;
643 placeholderSpanItem->placeholderIndex += preParagraphsPlaceholderCount_;
644 placeholderSpanItem->content = " ";
645 spanTextLength += 1;
646 placeholderSpanItem->length = 1;
647 placeholderSpanItem->position = spanTextLength;
648 }
649
UpdateParagraphByCustomSpan(RefPtr<CustomSpanItem> & customSpanItem,LayoutWrapper * layoutWrapper,const RefPtr<Paragraph> & paragraph,int32_t & spanTextLength,CustomSpanPlaceholderInfo & customSpanPlaceholder)650 void MultipleParagraphLayoutAlgorithm::UpdateParagraphByCustomSpan(RefPtr<CustomSpanItem>& customSpanItem,
651 LayoutWrapper* layoutWrapper, const RefPtr<Paragraph>& paragraph, int32_t& spanTextLength,
652 CustomSpanPlaceholderInfo& customSpanPlaceholder)
653 {
654 CHECK_NULL_VOID(layoutWrapper);
655 auto layoutProperty = layoutWrapper->GetLayoutProperty();
656 CHECK_NULL_VOID(layoutProperty);
657 auto context = PipelineBase::GetCurrentContextSafely();
658 CHECK_NULL_VOID(context);
659 auto theme = context->GetTheme<TextTheme>();
660 CHECK_NULL_VOID(theme);
661 auto fontSize = theme->GetTextStyle().GetFontSize().ConvertToVp() * context->GetFontScale();
662 auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutProperty);
663 auto fontSizeOpt = textLayoutProperty->GetFontSize();
664 if (fontSizeOpt.has_value()) {
665 fontSize = fontSizeOpt.value().ConvertToVp() * context->GetFontScale();
666 }
667 auto width = 0.0f;
668 auto height = 0.0f;
669 if (customSpanItem->onMeasure.has_value()) {
670 auto onMeasure = customSpanItem->onMeasure.value();
671 CustomSpanMetrics customSpanMetrics = onMeasure({ fontSize });
672 width = static_cast<float>(customSpanMetrics.width * context->GetDipScale());
673 height = static_cast<float>(
674 customSpanMetrics.height.value_or(fontSize / context->GetFontScale()) * context->GetDipScale());
675 }
676 PlaceholderStyle placeholderStyle;
677 placeholderStyle.width = width;
678 placeholderStyle.height = height;
679 placeholderStyle.verticalAlign = VerticalAlign::NONE;
680 customSpanItem->placeholderIndex =
681 customSpanItem->UpdateParagraph(nullptr, paragraph, isSpanStringMode_, placeholderStyle);
682 currentParagraphPlaceholderCount_++;
683 customSpanItem->placeholderIndex += preParagraphsPlaceholderCount_;
684 customSpanItem->content = " ";
685 spanTextLength += 1;
686 customSpanItem->length = 1;
687 customSpanItem->position = spanTextLength;
688 if (customSpanItem->onDraw.has_value()) {
689 customSpanPlaceholder.onDraw = customSpanItem->onDraw.value();
690 }
691 customSpanPlaceholder.customSpanIndex = customSpanItem->placeholderIndex;
692 }
693
ApplyIndent(ParagraphStyle & paragraphStyle,const RefPtr<Paragraph> & paragraph,double width,const TextStyle & textStyle)694 void MultipleParagraphLayoutAlgorithm::ApplyIndent(
695 ParagraphStyle& paragraphStyle, const RefPtr<Paragraph>& paragraph, double width, const TextStyle& textStyle)
696 {
697 auto indentValue = paragraphStyle.indent;
698 CHECK_NULL_VOID(paragraph);
699 double value = 0.0;
700 if (GreatNotEqual(indentValue.Value(), 0.0)) {
701 // first line indent
702 auto pipeline = PipelineContext::GetCurrentContextSafely();
703 CHECK_NULL_VOID(pipeline);
704 if (indentValue.Unit() != DimensionUnit::PERCENT) {
705 value = indentValue.ConvertToPxDistribute(
706 textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
707 } else {
708 value = static_cast<float>(width * indentValue.Value());
709 paragraphStyle.indent = Dimension(value);
710 }
711 }
712 auto indent = static_cast<float>(value);
713 auto leadingMarginValue = 0.0f;
714 std::vector<float> indents;
715 if (paragraphStyle.leadingMargin.has_value()) {
716 leadingMarginValue = paragraphStyle.leadingMargin->size.Width().ConvertToPxDistribute(
717 textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
718 }
719 indents.emplace_back(indent + leadingMarginValue);
720 indents.emplace_back(leadingMarginValue);
721 paragraph->SetIndents(indents);
722 }
723
UpdateSymbolSpanEffect(RefPtr<FrameNode> & frameNode,const RefPtr<Paragraph> & paragraph,const std::list<RefPtr<SpanItem>> & spans)724 void MultipleParagraphLayoutAlgorithm::UpdateSymbolSpanEffect(
725 RefPtr<FrameNode>& frameNode, const RefPtr<Paragraph>& paragraph, const std::list<RefPtr<SpanItem>>& spans)
726 {
727 for (const auto& child : spans) {
728 if (!child || child->unicode == 0) {
729 continue;
730 }
731 if (child->GetTextStyle()->isSymbolGlyph_) {
732 paragraph->SetParagraphSymbolAnimation(frameNode);
733 return;
734 }
735 }
736 }
737
GetMaxMeasureSize(const LayoutConstraintF & contentConstraint)738 SizeF MultipleParagraphLayoutAlgorithm::GetMaxMeasureSize(const LayoutConstraintF& contentConstraint)
739 {
740 auto maxSize = contentConstraint.selfIdealSize;
741 maxSize.UpdateIllegalSizeWithCheck(contentConstraint.maxSize);
742 return maxSize.ConvertToSizeT();
743 }
744 } // namespace OHOS::Ace::NG
745