1 /*
2 * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "core/components_ng/pattern/text/text_layout_algorithm.h"
17
18 #include "base/geometry/dimension.h"
19 #include "base/log/ace_trace.h"
20 #include "base/utils/utf_helper.h"
21 #include "core/components/common/properties/alignment.h"
22 #include "core/components/common/properties/text_style.h"
23 #include "core/components/hyperlink/hyperlink_theme.h"
24 #include "core/components/text/text_theme.h"
25 #include "core/components_ng/pattern/text/text_base.h"
26 #include "core/components_ng/pattern/text/text_pattern.h"
27 #include "core/components_ng/pattern/text/paragraph_util.h"
28 #ifdef ENABLE_ROSEN_BACKEND
29 #include "render_service_client/core/ui/rs_ui_director.h"
30 #endif
31
32 namespace OHOS::Ace::NG {
33 namespace {
34 constexpr int32_t HUNDRED = 100;
35 constexpr int32_t TWENTY = 20;
36
GetAdaptedMaxLines(const TextStyle & textStyle,const LayoutConstraintF & contentConstraint)37 uint32_t GetAdaptedMaxLines(const TextStyle& textStyle, const LayoutConstraintF& contentConstraint)
38 {
39 double minTextSizeHeight = textStyle.GetAdaptMinFontSize().ConvertToPxDistribute(
40 textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
41 if (LessOrEqual(minTextSizeHeight, 0.0)) {
42 minTextSizeHeight = textStyle.GetFontSize().ConvertToPxDistribute(
43 textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
44 }
45 double lineHeight = minTextSizeHeight;
46 if (textStyle.HasHeightOverride()) {
47 lineHeight = textStyle.GetLineHeight().Unit() == DimensionUnit::PERCENT
48 ? textStyle.GetLineHeight().ConvertToPxWithSize(contentConstraint.maxSize.Height())
49 : textStyle.GetLineHeight().ConvertToPxDistribute(
50 textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
51 }
52 // plus extraLine to ensure maxlines -1 is the next maxline to try for layout
53 uint32_t maxLines = contentConstraint.maxSize.Height() / (lineHeight) + 1;
54 return std::max(maxLines, static_cast<uint32_t>(0));
55 }
56 }; // namespace
57
TextLayoutAlgorithm(std::list<RefPtr<SpanItem>> spans,RefPtr<ParagraphManager> pManager,bool isSpanStringMode,const TextStyle & textStyle,bool isMarquee)58 TextLayoutAlgorithm::TextLayoutAlgorithm(
59 std::list<RefPtr<SpanItem>> spans, RefPtr<ParagraphManager> pManager, bool isSpanStringMode,
60 const TextStyle& textStyle, bool isMarquee)
61 {
62 if (SystemProperties::GetTextTraceEnabled()) {
63 ACE_TEXT_SCOPED_TRACE("TextLayoutAlgorithm::TextLayoutAlgorithm[styleUid:%d][isSpanStringMode:%d][size:%d]",
64 textStyle.GetTextStyleUid(), isSpanStringMode, static_cast<int32_t>(spans.size()));
65 }
66 paragraphManager_ = pManager;
67 isSpanStringMode_ = isSpanStringMode;
68 textStyle_ = textStyle;
69
70 if (!isSpanStringMode) {
71 if (!spans.empty()) {
72 spans_.emplace_back(std::move(spans));
73 }
74 return;
75 }
76 if (spans.empty()) {
77 return;
78 }
79 if (isMarquee) {
80 for (const auto& span : spans) {
81 span->SetNeedRemoveNewLine(false);
82 }
83 spans_.emplace_back(std::move(spans));
84 return;
85 }
86 ParagraphUtil::ConstructParagraphSpanGroup(spans, spans_, spanStringHasMaxLines_);
87 }
88
89 TextLayoutAlgorithm::TextLayoutAlgorithm() = default;
90
OnReset()91 void TextLayoutAlgorithm::OnReset() {}
92
MeasureContent(const LayoutConstraintF & constraint,LayoutWrapper * layoutWrapper)93 std::optional<SizeF> TextLayoutAlgorithm::MeasureContent(
94 const LayoutConstraintF& constraint, LayoutWrapper* layoutWrapper)
95 {
96 auto host = layoutWrapper->GetHostNode();
97 CHECK_NULL_RETURN(host, std::nullopt);
98 auto pattern = host->GetPattern<TextPattern>();
99 CHECK_NULL_RETURN(pattern, std::nullopt);
100 auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
101 CHECK_NULL_RETURN(textLayoutProperty, std::nullopt);
102 auto contentConstraint = CalcContentConstraint(constraint, layoutWrapper);
103 ConstructTextStyles(contentConstraint, layoutWrapper, textStyle_);
104 if (isSpanStringMode_ && spanStringHasMaxLines_) {
105 textStyle_.SetMaxLines(UINT32_MAX);
106 }
107 // inheritTextStyle_ is used to control spans_ in versions below VERSION_EIGHTEEN, preventing them from
108 // adapting to font size automatically.
109 inheritTextStyle_ = textStyle_;
110 MeasureChildren(layoutWrapper, textStyle_);
111 CheckNeedReCreateParagraph(layoutWrapper, textStyle_);
112 ACE_SCOPED_TRACE("TextLayoutAlgorithm::MeasureContent[id:%d][needReCreateParagraph:%d][size:%d]", host->GetId(),
113 needReCreateParagraph_, static_cast<int32_t>(spans_.size()));
114 if (textStyle_.GetTextOverflow() == TextOverflow::MARQUEE) { // create a paragraph with all text in 1 line
115 isMarquee_ = true;
116 auto result = BuildTextRaceParagraph(textStyle_, textLayoutProperty, contentConstraint, layoutWrapper);
117 return result;
118 }
119 if (isSpanStringMode_ && host->LessThanAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN)) {
120 BuildParagraph(textStyle_, textLayoutProperty, contentConstraint, layoutWrapper);
121 } else {
122 if (!AddPropertiesAndAnimations(textStyle_, textLayoutProperty, contentConstraint, layoutWrapper)) {
123 return std::nullopt;
124 }
125 }
126 UpdateRelayoutShaderStyle(layoutWrapper);
127 baselineOffset_ = textStyle_.GetBaselineOffset().ConvertToPxDistribute(
128 textStyle_.GetMinFontScale(), textStyle_.GetMaxFontScale(), textStyle_.IsAllowScale());
129 if (NearZero(contentConstraint.maxSize.Height()) || NearZero(contentConstraint.maxSize.Width()) ||
130 IsParentSizeNearZero(contentConstraint, layoutWrapper)) {
131 return SizeF {};
132 }
133 CHECK_NULL_RETURN(paragraphManager_, std::nullopt);
134 #ifdef ENABLE_ROSEN_BACKEND
135 auto pipeline = host->GetContext();
136 auto fontManager = pipeline == nullptr ? nullptr : pipeline->GetFontManager();
137 if (fontManager != nullptr && Rosen::RSUIDirector::IsHybridRenderEnabled()) {
138 if (static_cast<uint32_t>(paragraphManager_->GetLineCount()) >=
139 Rosen::RSUIDirector::GetHybridRenderTextBlobLenCount()) {
140 fontManager->AddHybridRenderNode(host);
141 } else {
142 fontManager->RemoveHybridRenderNode(host);
143 }
144 }
145 #endif
146 auto height = paragraphManager_->GetHeight();
147 auto maxWidth = paragraphManager_->GetMaxWidth();
148 auto longestLine = paragraphManager_->GetLongestLine();
149 auto heightFinal = static_cast<float>(height + std::fabs(baselineOffset_));
150 if (contentConstraint.selfIdealSize.Height().has_value()) {
151 heightFinal = std::min(heightFinal, contentConstraint.selfIdealSize.Height().value());
152 } else {
153 heightFinal = std::min(heightFinal, contentConstraint.maxSize.Height());
154 }
155 if (host->GetTag() == V2::TEXT_ETS_TAG && textLayoutProperty->GetContent().value_or(u"").empty() &&
156 NonPositive(longestLine)) {
157 ACE_SCOPED_TRACE("TextHeightFinal [%f], TextContentWidth [%f], FontSize [%lf]", heightFinal, maxWidth,
158 textStyle_.GetFontSize().ConvertToPxDistribute(
159 textStyle_.GetMinFontScale(), textStyle_.GetMaxFontScale(), textStyle_.IsAllowScale()));
160 return SizeF {};
161 }
162 return SizeF(maxWidth, heightFinal);
163 }
164
UpdateRelayoutShaderStyle(LayoutWrapper * layoutWrapper)165 void TextLayoutAlgorithm::UpdateRelayoutShaderStyle(LayoutWrapper* layoutWrapper)
166 {
167 auto host = layoutWrapper->GetHostNode();
168 CHECK_NULL_VOID(host);
169 auto pattern = host->GetPattern<TextPattern>();
170 CHECK_NULL_VOID(pattern);
171 auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
172 CHECK_NULL_VOID(textLayoutProperty);
173 if (textStyle_.GetGradient().has_value() && !pattern->GetExternalParagraph()) {
174 RelayoutShaderStyle(textLayoutProperty);
175 }
176 }
177
RelayoutShaderStyle(const RefPtr<TextLayoutProperty> & layoutProperty)178 void TextLayoutAlgorithm::RelayoutShaderStyle(const RefPtr<TextLayoutProperty>& layoutProperty)
179 {
180 CHECK_NULL_VOID(paragraphManager_);
181 auto paragraphs = paragraphManager_->GetParagraphs();
182 if (spans_.empty()) {
183 for (auto pIter = paragraphs.begin(); pIter != paragraphs.end(); pIter++) {
184 auto paragraph = pIter->paragraph;
185 if (!paragraph) {
186 continue;
187 }
188 auto textStyle = textStyle_;
189 textStyle.SetForeGroundBrushBitMap();
190 paragraph->ReLayoutForeground(textStyle);
191 }
192 return;
193 }
194 if (!spans_.empty()) {
195 size_t itemIndex = -1;
196 for (auto pIter = paragraphs.begin(); pIter != paragraphs.end(); pIter++) {
197 ++itemIndex;
198 auto paragraph = pIter->paragraph;
199 if (!paragraph) {
200 continue;
201 }
202 if (itemIndex >= spans_.size()) {
203 return;
204 }
205 auto spans = spans_[itemIndex];
206 TextStyle textStyle;
207 if (!spans.empty() && spans.front() && spans.front()->GetTextStyle() &&
208 spans.front()->GetTextStyle()->GetGradient().has_value()) {
209 textStyle = spans.front()->GetTextStyle().value();
210 } else {
211 textStyle = textStyle_;
212 }
213 textStyle.SetForeGroundBrushBitMap();
214 paragraph->ReLayoutForeground(textStyle);
215 }
216 }
217 }
218
AddPropertiesAndAnimations(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & textLayoutProperty,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)219 bool TextLayoutAlgorithm::AddPropertiesAndAnimations(TextStyle& textStyle,
220 const RefPtr<TextLayoutProperty>& textLayoutProperty, const LayoutConstraintF& contentConstraint,
221 LayoutWrapper* layoutWrapper)
222 {
223 bool result = false;
224 switch (textLayoutProperty->GetHeightAdaptivePolicyValue(TextHeightAdaptivePolicy::MAX_LINES_FIRST)) {
225 case TextHeightAdaptivePolicy::MAX_LINES_FIRST:
226 result = BuildParagraph(textStyle, textLayoutProperty, contentConstraint, layoutWrapper);
227 break;
228 case TextHeightAdaptivePolicy::MIN_FONT_SIZE_FIRST:
229 result = BuildParagraphAdaptUseMinFontSize(textStyle, textLayoutProperty, contentConstraint, layoutWrapper);
230 break;
231 case TextHeightAdaptivePolicy::LAYOUT_CONSTRAINT_FIRST:
232 result =
233 BuildParagraphAdaptUseLayoutConstraint(textStyle, textLayoutProperty, contentConstraint, layoutWrapper);
234 break;
235 default:
236 break;
237 }
238 return result;
239 }
240
CheckNeedReCreateParagraph(LayoutWrapper * layoutWrapper,const TextStyle & textStyle)241 void TextLayoutAlgorithm::CheckNeedReCreateParagraph(LayoutWrapper* layoutWrapper, const TextStyle& textStyle)
242 {
243 auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
244 CHECK_NULL_VOID(textLayoutProperty);
245 auto frameNode = layoutWrapper->GetHostNode();
246 CHECK_NULL_VOID(frameNode);
247 auto textPattern = frameNode->GetPattern<TextPattern>();
248 CHECK_NULL_VOID(textPattern);
249 alwaysReCreateParagraph_ = AlwaysReCreateParagraph(layoutWrapper);
250 needReCreateParagraph_ = textLayoutProperty->GetNeedReCreateParagraphValue(false) ||
251 textStyle.NeedReCreateParagraph() || alwaysReCreateParagraph_;
252 }
253
ResetNeedReCreateParagraph(LayoutWrapper * layoutWrapper)254 void TextLayoutAlgorithm::ResetNeedReCreateParagraph(LayoutWrapper* layoutWrapper)
255 {
256 auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
257 CHECK_NULL_VOID(textLayoutProperty);
258 textLayoutProperty->ResetNeedReCreateParagraph();
259 CHECK_NULL_VOID(!alwaysReCreateParagraph_);
260 needReCreateParagraph_ = false;
261 }
262
AlwaysReCreateParagraph(LayoutWrapper * layoutWrapper)263 bool TextLayoutAlgorithm::AlwaysReCreateParagraph(LayoutWrapper* layoutWrapper)
264 {
265 auto frameNode = layoutWrapper->GetHostNode();
266 CHECK_NULL_RETURN(frameNode, false);
267 auto textPattern = frameNode->GetPattern<TextPattern>();
268 CHECK_NULL_RETURN(textPattern, false);
269 auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
270 CHECK_NULL_RETURN(textLayoutProperty, false);
271 return textPattern->GetExternalParagraph() || textPattern->NeedShowAIDetect() || isSpanStringMode_ ||
272 textPattern->IsDragging() ||
273 textLayoutProperty->GetEllipsisModeValue(EllipsisMode::TAIL) == EllipsisMode::MIDDLE ||
274 textPattern->IsSensitiveEnable() ||
275 textLayoutProperty->GetSymbolTypeValue(SymbolType::SYSTEM) == SymbolType::CUSTOM;
276 }
277
UpdateParagraphForAISpan(const TextStyle & textStyle,LayoutWrapper * layoutWrapper,const RefPtr<Paragraph> & paragraph)278 void TextLayoutAlgorithm::UpdateParagraphForAISpan(
279 const TextStyle& textStyle, LayoutWrapper* layoutWrapper, const RefPtr<Paragraph>& paragraph)
280 {
281 CHECK_NULL_VOID(layoutWrapper);
282 auto layoutProperty = layoutWrapper->GetLayoutProperty();
283 CHECK_NULL_VOID(layoutProperty);
284 auto frameNode = layoutWrapper->GetHostNode();
285 CHECK_NULL_VOID(frameNode);
286 auto pattern = frameNode->GetPattern<TextPattern>();
287 CHECK_NULL_VOID(pattern);
288 auto wTextForAI = pattern->GetTextForAI();
289 int32_t wTextForAILength = static_cast<int32_t>(wTextForAI.length());
290 int32_t preEnd = 0;
291 DragSpanPosition dragSpanPosition;
292 dragSpanPosition.dragStart = pattern->GetRecoverStart();
293 dragSpanPosition.dragEnd = pattern->GetRecoverEnd();
294 bool isDragging = pattern->IsDragging();
295 TextStyle aiSpanStyle = textStyle;
296 pattern->ModifyAISpanStyle(aiSpanStyle);
297 for (auto kv : pattern->GetAISpanMap()) {
298 if (preEnd >= wTextForAILength) {
299 break;
300 }
301 auto aiSpan = kv.second;
302 if (aiSpan.start < preEnd) {
303 TAG_LOGI(AceLogTag::ACE_TEXT, "Error prediction");
304 continue;
305 }
306 if (preEnd < aiSpan.start) {
307 dragSpanPosition.spanStart = preEnd;
308 dragSpanPosition.spanEnd = aiSpan.start;
309 GrayDisplayAISpan(dragSpanPosition, wTextForAI, textStyle, isDragging, paragraph);
310 }
311 preEnd = aiSpan.end;
312 dragSpanPosition.spanStart = aiSpan.start;
313 dragSpanPosition.spanEnd = aiSpan.end;
314 GrayDisplayAISpan(dragSpanPosition, wTextForAI, aiSpanStyle, isDragging, paragraph);
315 }
316 if (preEnd < wTextForAILength) {
317 dragSpanPosition.spanStart = preEnd;
318 dragSpanPosition.spanEnd = wTextForAILength;
319 GrayDisplayAISpan(dragSpanPosition, wTextForAI, textStyle, isDragging, paragraph);
320 }
321 }
322
GrayDisplayAISpan(const DragSpanPosition & dragSpanPosition,const std::u16string wTextForAI,const TextStyle & textStyle,bool isDragging,const RefPtr<Paragraph> & paragraph)323 void TextLayoutAlgorithm::GrayDisplayAISpan(const DragSpanPosition& dragSpanPosition, const std::u16string wTextForAI,
324 const TextStyle& textStyle, bool isDragging, const RefPtr<Paragraph>& paragraph)
325 {
326 int32_t dragStart = dragSpanPosition.dragStart;
327 int32_t dragEnd = dragSpanPosition.dragEnd;
328 int32_t spanStart = dragSpanPosition.spanStart;
329 int32_t spanEnd = dragSpanPosition.spanEnd;
330 std::vector<std::u16string> contents = {};
331 std::u16string firstParagraph = u"";
332 std::u16string secondParagraph = u"";
333 std::u16string thirdParagraph = u"";
334 if (dragStart > spanEnd || dragEnd < spanStart || !isDragging) {
335 firstParagraph = StringOutBoundProtection(spanStart, spanEnd - spanStart, wTextForAI);
336 } else if (spanStart <= dragStart && spanEnd >= dragStart && spanEnd <= dragEnd) {
337 firstParagraph = StringOutBoundProtection(spanStart, dragStart - spanStart, wTextForAI);
338 secondParagraph = StringOutBoundProtection(dragStart, spanEnd - dragStart, wTextForAI);
339 } else if (spanStart >= dragStart && spanEnd <= dragEnd) {
340 secondParagraph = StringOutBoundProtection(spanStart, spanEnd - spanStart, wTextForAI);
341 } else if (spanStart <= dragStart && spanEnd >= dragEnd) {
342 firstParagraph = StringOutBoundProtection(spanStart, dragStart - spanStart, wTextForAI);
343 secondParagraph = StringOutBoundProtection(dragStart, dragEnd - dragStart, wTextForAI);
344 thirdParagraph = StringOutBoundProtection(dragEnd, spanEnd - dragEnd, wTextForAI);
345 } else {
346 secondParagraph = StringOutBoundProtection(spanStart, dragEnd - spanStart, wTextForAI);
347 thirdParagraph = StringOutBoundProtection(dragEnd, spanEnd - dragEnd, wTextForAI);
348 }
349 contents = { firstParagraph, secondParagraph, thirdParagraph };
350 CreateParagraphDrag(textStyle, contents, paragraph);
351 }
352
StringOutBoundProtection(int32_t position,int32_t length,std::u16string wTextForAI)353 std::u16string TextLayoutAlgorithm::StringOutBoundProtection(int32_t position, int32_t length,
354 std::u16string wTextForAI)
355 {
356 int32_t wTextForAILength = static_cast<int32_t>(wTextForAI.length());
357 if (position >= 0 && position < wTextForAILength && length >= 0 && length <= wTextForAILength - position) {
358 return wTextForAI.substr(position, length);
359 }
360 return u"";
361 }
362
CreateParagraph(const TextStyle & textStyle,std::u16string content,LayoutWrapper * layoutWrapper,double maxWidth)363 bool TextLayoutAlgorithm::CreateParagraph(
364 const TextStyle& textStyle, std::u16string content, LayoutWrapper* layoutWrapper, double maxWidth)
365 {
366 if (!paragraphManager_) {
367 paragraphManager_ = AceType::MakeRefPtr<ParagraphManager>();
368 }
369 auto frameNode = layoutWrapper->GetHostNode();
370 CHECK_NULL_RETURN(frameNode, false);
371 auto pattern = frameNode->GetPattern<TextPattern>();
372 CHECK_NULL_RETURN(pattern, false);
373 pattern->ClearCustomSpanPlaceholderInfo();
374 if (pattern->IsSensitiveEnable()) {
375 UpdateSensitiveContent(content);
376 }
377 auto useExternalParagraph = pattern->GetExternalParagraph() && !pattern->NeedShowAIDetect();
378 auto externalParagraphStyle = pattern->GetExternalParagraphStyle();
379 auto paraStyle = ParagraphUtil::GetParagraphStyle(textStyle);
380 if (pattern->GetExternalParagraph()) {
381 if (!useExternalParagraph && externalParagraphStyle) {
382 paraStyle = externalParagraphStyle.value();
383 }
384 }
385 if (frameNode->GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN) || isSpanStringMode_) {
386 paraStyle.fontSize = textStyle.GetFontSize().ConvertToPxDistribute(
387 textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
388 }
389 paraStyle.leadingMarginAlign = Alignment::CENTER;
390 paraStyle.textStyleUid = frameNode->GetId();
391 // SymbolGlyph
392 if (frameNode->GetTag() == V2::SYMBOL_ETS_TAG) {
393 paragraphManager_->Reset();
394 return UpdateSymbolTextStyle(textStyle, paraStyle, layoutWrapper, frameNode);
395 }
396 if (spans_.empty() || useExternalParagraph) {
397 // only use for text.
398 return UpdateSingleParagraph(layoutWrapper, paraStyle, textStyle, content, maxWidth);
399 } else {
400 paragraphManager_->Reset();
401 return UpdateParagraphBySpan(layoutWrapper, paraStyle, maxWidth, textStyle);
402 }
403 }
404
UpdateSymbolTextStyle(const TextStyle & textStyle,const ParagraphStyle & paraStyle,LayoutWrapper * layoutWrapper,RefPtr<FrameNode> & frameNode)405 bool TextLayoutAlgorithm::UpdateSymbolTextStyle(const TextStyle& textStyle, const ParagraphStyle& paraStyle,
406 LayoutWrapper* layoutWrapper, RefPtr<FrameNode>& frameNode)
407 {
408 auto&& paragraph = Paragraph::Create(paraStyle, FontCollection::Current());
409 CHECK_NULL_RETURN(paragraph, false);
410 auto layoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
411 CHECK_NULL_RETURN(layoutProperty, false);
412 auto symbolSourceInfo = layoutProperty->GetSymbolSourceInfo();
413 CHECK_NULL_RETURN(symbolSourceInfo, false);
414 auto symbolType = textStyle.GetSymbolType();
415 std::vector<std::string> fontFamilies = textStyle.GetFontFamilies();
416 if (symbolType == SymbolType::CUSTOM && fontFamilies.empty()) {
417 return false;
418 }
419 paragraph->PushStyle(textStyle);
420 if (textStyle.GetSymbolEffectOptions().has_value()) {
421 auto symbolEffectOptions = layoutProperty->GetSymbolEffectOptionsValue(SymbolEffectOptions());
422 symbolEffectOptions.Reset();
423 layoutProperty->UpdateSymbolEffectOptions(symbolEffectOptions);
424 }
425 paragraph->AddSymbol(symbolSourceInfo->GetUnicode());
426 paragraph->PopStyle();
427 paragraph->Build();
428 paragraph->SetParagraphSymbolAnimation(frameNode);
429 paragraphManager_->AddParagraph({ .paragraph = paragraph, .paragraphStyle = paraStyle, .start = 0, .end = 2 });
430 return true;
431 }
432
CreateParagraphDrag(const TextStyle & textStyle,const std::vector<std::u16string> & contents,const RefPtr<Paragraph> & paragraph)433 void TextLayoutAlgorithm::CreateParagraphDrag(
434 const TextStyle& textStyle, const std::vector<std::u16string>& contents, const RefPtr<Paragraph>& paragraph)
435 {
436 TextStyle dragTextStyle = textStyle;
437 Color color = textStyle.GetTextColor().ChangeAlpha(DRAGGED_TEXT_TRANSPARENCY);
438 dragTextStyle.SetTextColor(color);
439 Color textDecorationColor = textStyle.GetTextDecorationColor().ChangeAlpha(DRAGGED_TEXT_TRANSPARENCY);
440 dragTextStyle.SetTextDecorationColor(textDecorationColor);
441 std::vector<TextStyle> textStyles { textStyle, dragTextStyle, textStyle };
442
443 CHECK_NULL_VOID(paragraph);
444 for (size_t i = 0; i < contents.size(); i++) {
445 std::u16string splitStr = contents[i];
446 if (splitStr.empty()) {
447 continue;
448 }
449 auto& style = textStyles[i];
450 paragraph->PushStyle(style);
451 StringUtils::TransformStrCase(splitStr, static_cast<int32_t>(style.GetTextCase()));
452 UtfUtils::HandleInvalidUTF16(reinterpret_cast<uint16_t*>(splitStr.data()), splitStr.length(), 0);
453 paragraph->AddText(splitStr);
454 paragraph->PopStyle();
455 }
456 }
457
ReLayoutParagraphs(const TextStyle & textStyle,LayoutWrapper * layoutWrapper,const SizeF & maxSize)458 bool TextLayoutAlgorithm::ReLayoutParagraphs(
459 const TextStyle& textStyle, LayoutWrapper* layoutWrapper, const SizeF& maxSize)
460 {
461 auto needReLayout = textStyle.NeedReLayout();
462 ParagraphStyle parStyle;
463 std::vector<TextStyle> textStyles;
464 if (!spans_.empty() && !isSpanStringMode_ && !needReCreateParagraph_) {
465 needReLayout |= ReLayoutParagraphBySpan(layoutWrapper, parStyle, textStyle, textStyles);
466 CHECK_NULL_RETURN(!needReCreateParagraph_, false);
467 } else if (!needReCreateParagraph_ && needReLayout) {
468 auto tempTextStyle = textStyle;
469 tempTextStyle.ResetTextBaselineOffset();
470 if (SystemProperties::GetTextTraceEnabled()) {
471 ACE_TEXT_SCOPED_TRACE("TextLayoutAlgorithm::ReLayoutParagraphs[id:%d][textStyleBitmap:%s]",
472 tempTextStyle.GetTextStyleUid(), tempTextStyle.GetReLayoutTextStyleBitmap().to_string().c_str());
473 }
474 textStyles.emplace_back(tempTextStyle);
475 parStyle = ParagraphUtil::GetParagraphStyle(textStyle);
476 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
477 parStyle.fontSize = textStyle.GetFontSize().ConvertToPxDistribute(
478 textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
479 }
480 }
481 auto frameNode = layoutWrapper->GetHostNode();
482 CHECK_NULL_RETURN(frameNode, false);
483 parStyle.textStyleUid = frameNode->GetId();
484 CHECK_NULL_RETURN(paragraphManager_, false);
485 auto paragraphInfo = paragraphManager_->GetParagraphs();
486 for (auto pIter = paragraphInfo.begin(); pIter != paragraphInfo.end(); pIter++) {
487 auto paragraph = pIter->paragraph;
488 CHECK_NULL_RETURN(paragraph, false);
489 if (!needReCreateParagraph_ && needReLayout) {
490 paragraph->ReLayout(maxSize.Width(), parStyle, textStyles);
491 } else {
492 paragraph->Layout(maxSize.Width());
493 }
494 }
495 return true;
496 }
497
CreateParagraphAndLayout(TextStyle & textStyle,const std::u16string & content,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper,bool needLayout)498 bool TextLayoutAlgorithm::CreateParagraphAndLayout(TextStyle& textStyle, const std::u16string& content,
499 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper, bool needLayout)
500 {
501 auto maxSize = MultipleParagraphLayoutAlgorithm::GetMaxMeasureSize(contentConstraint);
502 auto needReLayout = textStyle.NeedReLayout();
503 if (SystemProperties::GetTextTraceEnabled()) {
504 ACE_TEXT_SCOPED_TRACE("TextLayoutAlgorithm::CreateParagraphAndLayout[contentConstraint:%s][maxSize:%s][Len:%d]["
505 "needReCreateParagraph:%d][needReLayout:%d][fontSize:%s][fontColor:%s]",
506 contentConstraint.ToString().c_str(), maxSize.ToString().c_str(), static_cast<int32_t>(content.length()),
507 needReCreateParagraph_, needReLayout, textStyle.GetFontSize().ToString().c_str(),
508 textStyle.GetTextColor().ColorToString().c_str());
509 }
510 if (needReCreateParagraph_ && !CreateParagraph(textStyle, content, layoutWrapper, maxSize.Width())) {
511 return false;
512 }
513
514 if (needReCreateParagraph_) {
515 CHECK_NULL_RETURN(LayoutParagraphs(maxSize.Width()), false);
516 } else {
517 auto frameNode = layoutWrapper->GetHostNode();
518 CHECK_NULL_RETURN(frameNode, false);
519 auto pattern = frameNode->GetPattern<TextPattern>();
520 CHECK_NULL_RETURN(pattern, false);
521 pattern->RelayoutResetOrUpdateTextEffect();
522 if (!ReLayoutParagraphs(textStyle, layoutWrapper, maxSize)) {
523 CHECK_NULL_RETURN(CreateParagraph(textStyle, content, layoutWrapper, maxSize.Width()), false);
524 CHECK_NULL_RETURN(LayoutParagraphs(maxSize.Width()), false);
525 }
526 }
527 // Reset the flag after each paragraph layout.
528 ResetNeedReCreateParagraph(layoutWrapper);
529 textStyle.ResetReCreateAndReLayoutBitmap();
530 return true;
531 }
532
LayoutParagraphs(float maxWidth)533 bool TextLayoutAlgorithm::LayoutParagraphs(float maxWidth)
534 {
535 CHECK_NULL_RETURN(paragraphManager_, false);
536 auto paragraphInfo = paragraphManager_->GetParagraphs();
537 for (auto pIter = paragraphInfo.begin(); pIter != paragraphInfo.end(); pIter++) {
538 auto paragraph = pIter->paragraph;
539 CHECK_NULL_RETURN(paragraph, false);
540 paragraph->Layout(maxWidth);
541 }
542 return true;
543 }
544
GetContentOffset(LayoutWrapper * layoutWrapper)545 OffsetF TextLayoutAlgorithm::GetContentOffset(LayoutWrapper* layoutWrapper)
546 {
547 return SetContentOffset(layoutWrapper);
548 }
549
AdaptMinTextSize(TextStyle & textStyle,const std::u16string & content,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)550 bool TextLayoutAlgorithm::AdaptMinTextSize(TextStyle& textStyle, const std::u16string& content,
551 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
552 {
553 ACE_TEXT_SCOPED_TRACE("TextLayoutAlgorithm::AdaptMinTextSize[Length:%d]", static_cast<int32_t>(content.length()));
554 // IsNeedAdaptFontSize
555 double maxFontSize = 0.0;
556 double minFontSize = 0.0;
557 auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
558 CHECK_NULL_RETURN(pipeline, false);
559 GetAdaptMaxMinFontSize(textStyle, maxFontSize, minFontSize, contentConstraint);
560 if (!IsNeedAdaptFontSize(maxFontSize, minFontSize)) {
561 if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) {
562 TAG_LOGW(AceLogTag::ACE_TEXT, "create paragraph fail, contentConstraint:%{public}s",
563 contentConstraint.ToString().c_str());
564 return false;
565 }
566 return true;
567 }
568 if (NonPositive(contentConstraint.maxSize.Width())) {
569 textStyle.SetFontSize(Dimension(minFontSize));
570 return CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper);
571 }
572 // Get suitableSize and set
573 auto ret = GetSuitableSize(textStyle, content, contentConstraint, layoutWrapper);
574 if (!ret.first) {
575 textStyle.SetFontSize(Dimension(minFontSize));
576 return CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper);
577 } else if (ret.first && NearEqual(textStyle.GetFontSize().Value(), ret.second)) {
578 return true; // The font is already set, no need to call CreateParagraphAndLayout again.
579 } else {
580 textStyle.SetFontSize(Dimension(ret.second));
581 return CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper);
582 }
583 }
584
585 /**
586 * brief: Find the optimal font size within the range [minFontSize, maxFontSize].
587 * return: std::pair<bool, double>
588 * - first: A boolean indicating whether a suitable size was found (true if found, false otherwise).
589 * - second: The optimal font size if found, valid only when first is true.
590 */
GetSuitableSize(TextStyle & textStyle,const std::u16string & content,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)591 std::pair<bool, double> TextLayoutAlgorithm::GetSuitableSize(TextStyle& textStyle, const std::u16string& content,
592 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
593 {
594 double maxFontSize = 0.0;
595 double minFontSize = 0.0;
596 GetAdaptMaxMinFontSize(textStyle, maxFontSize, minFontSize, contentConstraint);
597 auto step = Dimension(1.0, DimensionUnit::PX);
598
599 if (GreatNotEqual(textStyle.GetAdaptFontSizeStep().Value(), 0.0)) {
600 step = textStyle.GetAdaptFontSizeStep();
601 }
602 double stepSize = step.ConvertToPxDistribute(textStyle.GetMinFontScale(),
603 textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
604 if (NearEqual(stepSize, 0.0)) {
605 return {false, 0.0};
606 }
607 int32_t stepCount = (maxFontSize - minFontSize) / stepSize;
608
609 // Compare time complexity: stepCount/2 < log(stepCount)+1, exp2 is fast.
610 if (step.GetAdaptDimensionUnit(step) != DimensionUnit::PX && exp2(stepCount / 2 - 1) < stepCount) {
611 return GetSuitableSizeLD(textStyle, content, contentConstraint, layoutWrapper, stepSize);
612 } else {
613 return GetSuitableSizeBS(textStyle, content, contentConstraint, layoutWrapper, stepSize);
614 }
615 }
616
GetSuitableSizeLD(TextStyle & textStyle,const std::u16string & content,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper,double stepSize)617 std::pair<bool, double> TextLayoutAlgorithm::GetSuitableSizeLD(TextStyle& textStyle, const std::u16string& content,
618 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper, double stepSize)
619 {
620 double maxFontSize = 0.0;
621 double minFontSize = 0.0;
622 GetAdaptMaxMinFontSize(textStyle, maxFontSize, minFontSize, contentConstraint);
623 auto maxSize = MultipleParagraphLayoutAlgorithm::GetMaxMeasureSize(contentConstraint);
624
625 if (NearEqual(stepSize, 0.0)) {
626 return {false, 0.0};
627 }
628 double suitableSize = maxFontSize;
629 uint32_t suitCount = 0;
630 while (GreatOrEqual(suitableSize, minFontSize)) {
631 textStyle.SetFontSize(Dimension(suitableSize));
632 if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) {
633 return {false, 0.0};
634 }
635 if (!DidExceedMaxLines(maxSize)) {
636 return {true, suitableSize};
637 }
638 if (suitCount % HUNDRED == 0) {
639 auto host = layoutWrapper->GetHostNode();
640 CHECK_NULL_RETURN(host, {});
641 TAG_LOGW(AceLogTag::ACE_TEXT,
642 "suit layout:%{public}d, [id:%{public}d, suitSize:%{public}f, minFontSize:%{public}f, "
643 "stepSize:%{public}f]",
644 suitCount, host->GetId(), suitableSize, minFontSize, stepSize);
645 }
646 suitCount++;
647 suitableSize -= stepSize;
648 }
649 return {false, 0.0};
650 }
651
GetSuitableSizeBS(TextStyle & textStyle,const std::u16string & content,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper,double stepSize)652 std::pair<bool, double> TextLayoutAlgorithm::GetSuitableSizeBS(TextStyle& textStyle, const std::u16string& content,
653 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper, double stepSize)
654 {
655 double maxFontSize = 0.0;
656 double minFontSize = 0.0;
657 GetAdaptMaxMinFontSize(textStyle, maxFontSize, minFontSize, contentConstraint);
658 auto maxSize = MultipleParagraphLayoutAlgorithm::GetMaxMeasureSize(contentConstraint);
659
660 // Boundary check: for efficiency and to ensure the optimal size is within [minFontSize, maxFontSize].
661 textStyle.SetFontSize(Dimension(maxFontSize));
662 if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) {
663 TAG_LOGW(AceLogTag::ACE_TEXT, "GetSuitableSizeBS create paragraph fail");
664 return {false, 0.0};
665 }
666 if (!DidExceedMaxLines(maxSize)) {
667 return {true, maxFontSize};
668 }
669
670 if (NearEqual(stepSize, 0.0)) {
671 return {false, 0.0};
672 }
673 int32_t stepCount = (maxFontSize - minFontSize) / stepSize;
674
675 // Binary search: to find the optimal size within [minFontSize, maxFontSize].
676 int32_t leftBound = 0;
677 int32_t rightBound = stepCount;
678 int32_t mid = (leftBound + rightBound) / 2;
679 uint32_t suitCount = 0;
680 while (leftBound < rightBound) {
681 double suitSz = minFontSize + mid * stepSize;
682 textStyle.SetFontSize(Dimension(suitSz));
683 if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) {
684 return {false, 0.0};
685 }
686 if (!DidExceedMaxLines(maxSize)) {
687 leftBound = mid;
688 } else {
689 rightBound = mid - 1;
690 }
691 if (suitCount % TWENTY == 0) {
692 auto host = layoutWrapper->GetHostNode();
693 CHECK_NULL_RETURN(host, {});
694 TAG_LOGI(AceLogTag::ACE_TEXT,
695 "suit layout:%{public}d, [id:%{public}d, suitSz:%{public}f, stepCount:%{public}d, stepSize:%{public}f]",
696 suitCount, host->GetId(), suitSz, stepCount, stepSize);
697 }
698 suitCount++;
699 mid = (leftBound + rightBound + 1) / 2;
700 }
701 return {true, minFontSize + leftBound * stepSize};
702 }
703
GetBaselineOffset() const704 float TextLayoutAlgorithm::GetBaselineOffset() const
705 {
706 return baselineOffset_;
707 }
708
UpdateSingleParagraph(LayoutWrapper * layoutWrapper,ParagraphStyle paraStyle,const TextStyle & textStyle,const std::u16string & content,double maxWidth)709 bool TextLayoutAlgorithm::UpdateSingleParagraph(LayoutWrapper* layoutWrapper, ParagraphStyle paraStyle,
710 const TextStyle& textStyle, const std::u16string& content, double maxWidth)
711 {
712 auto host = layoutWrapper->GetHostNode();
713 CHECK_NULL_RETURN(host, false);
714 ACE_TEXT_SCOPED_TRACE("TextLayoutAlgorithm::UpdateSingleParagraph[id:%d][length:%d][w:%f]", host->GetId(),
715 static_cast<int32_t>(content.length()), maxWidth);
716 auto pattern = host->GetPattern<TextPattern>();
717 CHECK_NULL_RETURN(pattern, false);
718 CHECK_NULL_RETURN(paragraphManager_, false);
719 RefPtr<Paragraph> oldParagraph;
720 auto externalParagraph = pattern->GetExternalParagraph();
721 RefPtr<Paragraph> paragraph;
722 if (externalParagraph) {
723 paragraph = Paragraph::Create(externalParagraph.value());
724 } else {
725 paragraph = Paragraph::Create(paraStyle, FontCollection::Current());
726 auto paragraphs = paragraphManager_->GetParagraphs();
727 if (!paragraphs.empty()) {
728 oldParagraph = paragraphs.front().paragraph;
729 }
730 }
731 paragraphManager_->Reset();
732 CHECK_NULL_RETURN(paragraph, false);
733 auto textStyleTmp = textStyle;
734 textStyleTmp.ResetTextBaselineOffset();
735 paragraph->PushStyle(textStyleTmp);
736 if (pattern->NeedShowAIDetect()) {
737 UpdateParagraphForAISpan(textStyle, layoutWrapper, paragraph);
738 } else {
739 if (pattern->IsDragging()) {
740 auto dragContents = pattern->GetDragContents();
741 CreateParagraphDrag(textStyleTmp, dragContents, paragraph);
742 } else {
743 auto value = content;
744 StringUtils::TransformStrCase(value, static_cast<int32_t>(textStyle.GetTextCase()));
745 UtfUtils::HandleInvalidUTF16(reinterpret_cast<uint16_t*>(value.data()), value.length(), 0);
746 paragraph->AddText(value);
747 }
748 }
749 paragraph->Build();
750 if (paragraph) {
751 CreateOrUpdateTextEffect(oldParagraph, paragraph, pattern, content);
752 }
753 ParagraphUtil::ApplyIndent(paraStyle, paragraph, maxWidth, textStyle, GetIndentMaxWidth(maxWidth));
754 paragraphManager_->AddParagraph({ .paragraph = paragraph,
755 .paragraphStyle = paraStyle,
756 .start = 0,
757 .end = content.length() });
758 return true;
759 }
760
CreateOrUpdateTextEffect(const RefPtr<Paragraph> & oldParagraph,const RefPtr<Paragraph> & newParagraph,const RefPtr<TextPattern> & textPattern,const std::u16string & content)761 void TextLayoutAlgorithm::CreateOrUpdateTextEffect(const RefPtr<Paragraph>& oldParagraph,
762 const RefPtr<Paragraph>& newParagraph, const RefPtr<TextPattern>& textPattern, const std::u16string& content)
763 {
764 bool needUpdateTypography = false;
765 auto textEffect = textPattern->GetOrCreateTextEffect(content, needUpdateTypography);
766 CHECK_NULL_VOID(textEffect);
767 auto frameNode = textPattern->GetHost();
768 CHECK_NULL_VOID(frameNode);
769 if (SystemProperties::GetTextTraceEnabled()) {
770 ACE_TEXT_SCOPED_TRACE(
771 "TextLayoutAlgorithm::CreateOrUpdateTextEffect[id:%d][needUpdateTypography:%d][content:%s]",
772 frameNode->GetId(), needUpdateTypography, UtfUtils::Str16DebugToStr8(content).c_str());
773 }
774 newParagraph->SetParagraphSymbolAnimation(frameNode);
775 if (needUpdateTypography && oldParagraph) {
776 std::vector<std::pair<RefPtr<Paragraph>, RefPtr<Paragraph>>> paragraphs;
777 auto pair = std::make_pair(oldParagraph, newParagraph);
778 paragraphs.emplace_back(pair);
779 textEffect->UpdateTypography(paragraphs);
780 } else if (!needUpdateTypography) {
781 std::vector<RefPtr<Paragraph>> paragraphs;
782 paragraphs.emplace_back(newParagraph);
783 textEffect->AppendTypography(paragraphs);
784 }
785 }
786
BuildParagraph(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)787 bool TextLayoutAlgorithm::BuildParagraph(TextStyle& textStyle, const RefPtr<TextLayoutProperty>& layoutProperty,
788 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
789 {
790 auto host = layoutWrapper->GetHostNode();
791 CHECK_NULL_RETURN(host, false);
792 if (!textStyle.GetAdaptTextSize() ||
793 (!spans_.empty() && host->LessThanAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN))) {
794 if (!CreateParagraphAndLayout(
795 textStyle, layoutProperty->GetContent().value_or(u""), contentConstraint, layoutWrapper)) {
796 TAG_LOGE(AceLogTag::ACE_TEXT, "create paragraph error");
797 return false;
798 }
799 } else {
800 if (!AdaptMinTextSize(
801 textStyle, layoutProperty->GetContent().value_or(u""), contentConstraint, layoutWrapper)) {
802 return false;
803 }
804 }
805 return ParagraphReLayout(contentConstraint);
806 }
807
BuildParagraphAdaptUseMinFontSize(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)808 bool TextLayoutAlgorithm::BuildParagraphAdaptUseMinFontSize(TextStyle& textStyle,
809 const RefPtr<TextLayoutProperty>& layoutProperty, const LayoutConstraintF& contentConstraint,
810 LayoutWrapper* layoutWrapper)
811 {
812 if (!AdaptMaxTextSize(textStyle, layoutProperty->GetContent().value_or(u""), contentConstraint, layoutWrapper)) {
813 return false;
814 }
815 return ParagraphReLayout(contentConstraint);
816 }
817
BuildParagraphAdaptUseLayoutConstraint(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)818 bool TextLayoutAlgorithm::BuildParagraphAdaptUseLayoutConstraint(TextStyle& textStyle,
819 const RefPtr<TextLayoutProperty>& layoutProperty, const LayoutConstraintF& contentConstraint,
820 LayoutWrapper* layoutWrapper)
821 {
822 // Create the paragraph and obtain the height.
823 if (!BuildParagraph(textStyle, layoutProperty, contentConstraint, layoutWrapper)) {
824 return false;
825 }
826
827 CHECK_NULL_RETURN(paragraphManager_, false);
828 if (textStyle.GetMaxLines() == UINT32_MAX) {
829 uint32_t maxLines = GetAdaptedMaxLines(textStyle, contentConstraint);
830 textStyle.SetMaxLines(maxLines);
831 }
832 auto lineCount = static_cast<uint32_t>(paragraphManager_->GetLineCount());
833 lineCount = std::max(std::min(textStyle.GetMaxLines(), lineCount), static_cast<uint32_t>(0));
834 textStyle.SetMaxLines(lineCount);
835 auto disableAdaptTextSize = textStyle.GetAdaptTextSize();
836 if (disableAdaptTextSize) {
837 textStyle.DisableAdaptTextSize();
838 }
839
840 auto height = static_cast<float>(paragraphManager_->GetHeight());
841 uint32_t adaptCount = 0;
842 while (GreatNotEqual(height, contentConstraint.maxSize.Height())) {
843 auto maxLines = textStyle.GetMaxLines();
844 if (maxLines == 0) {
845 break;
846 } else {
847 maxLines = textStyle.GetMaxLines() - 1;
848 textStyle.SetMaxLines(maxLines);
849 }
850 if (!BuildParagraph(textStyle, layoutProperty, contentConstraint, layoutWrapper)) {
851 if (disableAdaptTextSize) {
852 textStyle.SetAdaptTextSize(true);
853 }
854 return false;
855 }
856 if (adaptCount % HUNDRED == 0) {
857 auto host = layoutWrapper->GetHostNode();
858 CHECK_NULL_RETURN(host, {});
859 TAG_LOGW(AceLogTag::ACE_TEXT,
860 "AdaptLayout:%{public}d, [id:%{public}d, height:%{public}f, constraint:%{public}s, "
861 "maxlines:%{public}d]",
862 adaptCount, host->GetId(), height, contentConstraint.ToString().c_str(), maxLines);
863 }
864 adaptCount++;
865 height = static_cast<float>(paragraphManager_->GetHeight());
866 }
867 if (disableAdaptTextSize) {
868 textStyle.SetAdaptTextSize(true);
869 }
870 return true;
871 }
872
BuildTextRaceParagraph(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)873 std::optional<SizeF> TextLayoutAlgorithm::BuildTextRaceParagraph(TextStyle& textStyle,
874 const RefPtr<TextLayoutProperty>& layoutProperty, const LayoutConstraintF& contentConstraint,
875 LayoutWrapper* layoutWrapper)
876 {
877 // create a paragraph with all text in 1 line
878 textStyle.SetTextOverflow(TextOverflow::CLIP);
879 textStyle.SetMaxLines(1);
880 textStyle.SetTextIndent(Dimension(0.0f));
881 std::u16string content = layoutProperty->GetContent().value_or(u"");
882 std::replace(content.begin(), content.end(), u'\n', u' ');
883 if (!textStyle.GetAdaptTextSize()) {
884 if (!CreateParagraph(textStyle, content, layoutWrapper)) {
885 return std::nullopt;
886 }
887 textStyle.ResetReCreateAndReLayoutBitmap();
888 } else {
889 if (!AdaptMinTextSize(textStyle, content, contentConstraint, layoutWrapper)) {
890 return std::nullopt;
891 }
892 }
893 layoutProperty->OnPropertyChangeMeasure();
894
895 textStyle_ = textStyle;
896 auto paragraph = GetSingleParagraph();
897 // layout the paragraph to the width of text
898 paragraph->Layout(std::numeric_limits<float>::max());
899 float paragraphWidth = paragraph->GetLongestLineWithIndent();
900 if (contentConstraint.selfIdealSize.Width().has_value()) {
901 paragraphWidth = std::max(contentConstraint.selfIdealSize.Width().value(), paragraphWidth);
902 } else {
903 paragraphWidth = std::max(contentConstraint.minSize.Width(), paragraphWidth);
904 }
905 paragraphWidth = std::ceil(paragraphWidth);
906 paragraph->Layout(paragraphWidth);
907 UpdateRelayoutShaderStyle(layoutWrapper);
908
909 auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
910 // calculate the content size
911 auto height = static_cast<float>(paragraph->GetHeight());
912 baselineOffset_ = static_cast<float>(
913 layoutProperty->GetBaselineOffsetValue(Dimension())
914 .ConvertToPxDistribute(textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale()));
915 float heightFinal =
916 std::min(static_cast<float>(height + std::fabs(baselineOffset_)), contentConstraint.maxSize.Height());
917
918 float widthFinal = paragraphWidth;
919 if (contentConstraint.selfIdealSize.Width().has_value()) {
920 if (contentConstraint.selfIdealSize.Width().value() < paragraphWidth) {
921 widthFinal = contentConstraint.selfIdealSize.Width().value();
922 }
923 } else if (contentConstraint.maxSize.Width() < paragraphWidth) {
924 widthFinal = contentConstraint.maxSize.Width();
925 }
926 return SizeF(widthFinal, heightFinal);
927 }
928
AdaptMaxTextSize(TextStyle & textStyle,const std::u16string & content,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)929 bool TextLayoutAlgorithm::AdaptMaxTextSize(TextStyle& textStyle, const std::u16string& content,
930 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
931 {
932 constexpr Dimension ADAPT_UNIT = 1.0_fp;
933 auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
934 CHECK_NULL_RETURN(textLayoutProperty, false);
935 auto step = textLayoutProperty->GetAdaptFontSizeStepValue(ADAPT_UNIT);
936 return AdaptMaxFontSize(textStyle, content, step, contentConstraint, layoutWrapper);
937 }
938
UpdateSensitiveContent(std::u16string & content)939 void TextLayoutAlgorithm::UpdateSensitiveContent(std::u16string& content)
940 {
941 std::replace_if(
942 content.begin(), content.end(),
943 [](char16_t ch) {
944 return ch != u'\n';
945 }, u'-');
946 }
947
GetTextStyle() const948 const TextStyle& TextLayoutAlgorithm::GetTextStyle() const
949 {
950 return textStyle_;
951 }
952
GetLineCount() const953 size_t TextLayoutAlgorithm::GetLineCount() const
954 {
955 size_t count = 0;
956 CHECK_NULL_RETURN(paragraphManager_, 0);
957 auto paragraphInfo = paragraphManager_->GetParagraphs();
958 for (auto pIter = paragraphInfo.begin(); pIter != paragraphInfo.end(); pIter++) {
959 auto paragraph = pIter->paragraph;
960 CHECK_NULL_RETURN(paragraph, 0);
961 count += paragraph->GetLineCount();
962 }
963 return count;
964 }
965
DidExceedMaxLines(const SizeF & maxSize)966 bool TextLayoutAlgorithm::DidExceedMaxLines(const SizeF& maxSize)
967 {
968 CHECK_NULL_RETURN(paragraphManager_, false);
969 bool didExceedMaxLines = paragraphManager_->DidExceedMaxLinesInner();
970 didExceedMaxLines = didExceedMaxLines || GreatNotEqual(paragraphManager_->GetHeight(), maxSize.Height());
971 didExceedMaxLines =
972 didExceedMaxLines || GreatNotEqual(paragraphManager_->GetLongestLineWithIndent(), maxSize.Width());
973 return didExceedMaxLines;
974 }
975
IsAdaptExceedLimit(const SizeF & maxSize)976 bool TextLayoutAlgorithm::IsAdaptExceedLimit(const SizeF& maxSize)
977 {
978 CHECK_NULL_RETURN(paragraphManager_, false);
979 return (paragraphManager_->GetLineCount() > 1) || paragraphManager_->DidExceedMaxLinesInner() ||
980 GreatNotEqual(paragraphManager_->GetLongestLineWithIndent(), maxSize.Width());
981 }
982
IsParentSizeNearZero(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)983 bool TextLayoutAlgorithm::IsParentSizeNearZero(const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
984 {
985 auto widthPolicy = TextBase::GetLayoutCalPolicy(layoutWrapper, true);
986 if (widthPolicy == LayoutCalPolicy::MATCH_PARENT &&
987 (contentConstraint.parentIdealSize.Width().has_value() &&
988 NearZero(contentConstraint.parentIdealSize.Width().value()))) {
989 return true;
990 }
991 auto heightPolicy = TextBase::GetLayoutCalPolicy(layoutWrapper, false);
992 if (heightPolicy == LayoutCalPolicy::MATCH_PARENT &&
993 (contentConstraint.parentIdealSize.Height().has_value() &&
994 NearZero(contentConstraint.parentIdealSize.Height().value()))) {
995 return true;
996 }
997 return false;
998 }
999
IsFixIdealSizeAndNoMaxSize(LayoutWrapper * layoutWrapper,bool isWidth)1000 bool TextLayoutAlgorithm::IsFixIdealSizeAndNoMaxSize(LayoutWrapper* layoutWrapper, bool isWidth)
1001 {
1002 CHECK_NULL_RETURN(layoutWrapper, false);
1003 auto layoutPolicy = TextBase::GetLayoutCalPolicy(layoutWrapper, isWidth);
1004 if (layoutPolicy == LayoutCalPolicy::FIX_AT_IDEAL_SIZE) {
1005 const auto& layoutProperty = layoutWrapper->GetLayoutProperty();
1006 CHECK_NULL_RETURN(layoutProperty, false);
1007 const auto& calcLayoutConstraint = layoutProperty->GetCalcLayoutConstraint();
1008 CHECK_NULL_RETURN(calcLayoutConstraint, false);
1009 return isWidth ? !calcLayoutConstraint->maxSize->Width().has_value()
1010 : !calcLayoutConstraint->maxSize->Height().has_value();
1011 }
1012 return false;
1013 }
1014
CalcContentConstraint(const LayoutConstraintF & constraint,LayoutWrapper * layoutWrapper)1015 LayoutConstraintF TextLayoutAlgorithm::CalcContentConstraint(
1016 const LayoutConstraintF& constraint, LayoutWrapper* layoutWrapper)
1017 {
1018 LayoutConstraintF contentConstraint = constraint;
1019 CHECK_NULL_RETURN(layoutWrapper, contentConstraint);
1020 auto widthPolicy = TextBase::GetLayoutCalPolicy(layoutWrapper, true);
1021 isFixIdealSizeAndNoMaxWidth_ = IsFixIdealSizeAndNoMaxSize(layoutWrapper, true);
1022 if (widthPolicy == LayoutCalPolicy::FIX_AT_IDEAL_SIZE) {
1023 auto maxCalcWidth = GetCalcLayoutConstraintLength(layoutWrapper, true, true);
1024 if (maxCalcWidth) {
1025 contentConstraint.maxSize.SetWidth(maxCalcWidth.value());
1026 } else {
1027 contentConstraint.maxSize.SetWidth(std::numeric_limits<double>::infinity());
1028 }
1029 } else if (widthPolicy == LayoutCalPolicy::MATCH_PARENT && constraint.parentIdealSize.Width().has_value()) {
1030 contentConstraint.selfIdealSize.SetWidth(constraint.parentIdealSize.Width().value());
1031 }
1032 auto heightPolicy = TextBase::GetLayoutCalPolicy(layoutWrapper, false);
1033 if (heightPolicy == LayoutCalPolicy::FIX_AT_IDEAL_SIZE) {
1034 auto maxCalcHeight = GetCalcLayoutConstraintLength(layoutWrapper, true, false);
1035 if (maxCalcHeight) {
1036 contentConstraint.maxSize.SetHeight(maxCalcHeight.value());
1037 } else {
1038 contentConstraint.maxSize.SetHeight(std::numeric_limits<double>::infinity());
1039 }
1040 } else if (heightPolicy == LayoutCalPolicy::MATCH_PARENT && constraint.parentIdealSize.Height().has_value()) {
1041 contentConstraint.selfIdealSize.SetHeight(constraint.parentIdealSize.Height().value());
1042 }
1043 cachedCalcContentConstraint_ = contentConstraint;
1044 return contentConstraint;
1045 }
1046
IsNeedParagraphReLayout() const1047 bool TextLayoutAlgorithm::IsNeedParagraphReLayout() const
1048 {
1049 return isFixIdealSizeAndNoMaxWidth_;
1050 }
1051
GetIndentMaxWidth(double width) const1052 double TextLayoutAlgorithm::GetIndentMaxWidth(double width) const
1053 {
1054 if (isFixIdealSizeAndNoMaxWidth_ && cachedCalcContentConstraint_.has_value()) {
1055 return cachedCalcContentConstraint_.value().maxSize.Width();
1056 }
1057 return width;
1058 }
1059
GetCalcLayoutConstraintLength(LayoutWrapper * layoutWrapper,bool isMax,bool isWidth)1060 std::optional<float> TextLayoutAlgorithm::GetCalcLayoutConstraintLength(
1061 LayoutWrapper* layoutWrapper, bool isMax, bool isWidth)
1062 {
1063 auto layoutProperty = layoutWrapper->GetLayoutProperty();
1064 CHECK_NULL_RETURN(layoutProperty, std::nullopt);
1065 const auto& layoutCalcConstraint = layoutProperty->GetCalcLayoutConstraint();
1066 CHECK_NULL_RETURN(layoutCalcConstraint, std::nullopt);
1067 auto layoutConstraint = layoutProperty->GetLayoutConstraint();
1068 CHECK_NULL_RETURN(layoutConstraint, std::nullopt);
1069 auto calcLayoutConstraintMaxMinSize = isMax ? layoutCalcConstraint->maxSize : layoutCalcConstraint->minSize;
1070 CHECK_NULL_RETURN(calcLayoutConstraintMaxMinSize, std::nullopt);
1071 auto optionalCalcLength =
1072 isWidth ? calcLayoutConstraintMaxMinSize->Width() : calcLayoutConstraintMaxMinSize->Height();
1073 auto percentLength =
1074 isWidth ? layoutConstraint->percentReference.Width() : layoutConstraint->percentReference.Height();
1075 CHECK_NULL_RETURN(optionalCalcLength, std::nullopt);
1076 return ConvertToPx(optionalCalcLength, ScaleProperty::CreateScaleProperty(), percentLength);
1077 }
1078
MeasureWithFixAtIdealSize(LayoutWrapper * layoutWrapper)1079 void TextLayoutAlgorithm::MeasureWithFixAtIdealSize(LayoutWrapper* layoutWrapper)
1080 {
1081 CHECK_NULL_VOID(layoutWrapper);
1082 auto widthPolicy = TextBase::GetLayoutCalPolicy(layoutWrapper, true);
1083 auto heightPolicy = TextBase::GetLayoutCalPolicy(layoutWrapper, false);
1084 if (widthPolicy != LayoutCalPolicy::FIX_AT_IDEAL_SIZE && heightPolicy != LayoutCalPolicy::FIX_AT_IDEAL_SIZE) {
1085 return;
1086 }
1087 auto geometryNode = layoutWrapper->GetGeometryNode();
1088 CHECK_NULL_VOID(geometryNode);
1089 auto& content = geometryNode->GetContent();
1090 CHECK_NULL_VOID(content);
1091 auto layoutProperty = layoutWrapper->GetLayoutProperty();
1092 CHECK_NULL_VOID(layoutProperty);
1093 auto padding = layoutProperty->CreatePaddingAndBorder();
1094 auto contentSize = content->GetRect().GetSize();
1095 AddPaddingToSize(padding, contentSize);
1096 OptionalSizeF frameSize;
1097 frameSize.UpdateIllegalSizeWithCheck(contentSize);
1098 frameSize = UpdateOptionSizeByCalcLayoutConstraint(
1099 frameSize, layoutProperty->GetCalcLayoutConstraint(), layoutProperty->GetLayoutConstraint()->percentReference);
1100 auto fixSize = frameSize.ConvertToSizeT();
1101 auto measureSize = geometryNode->GetFrameSize();
1102 if (widthPolicy == LayoutCalPolicy::FIX_AT_IDEAL_SIZE) {
1103 measureSize.SetWidth(fixSize.Width());
1104 }
1105 if (heightPolicy == LayoutCalPolicy::FIX_AT_IDEAL_SIZE) {
1106 measureSize.SetHeight(fixSize.Height());
1107 }
1108 geometryNode->SetFrameSize(measureSize);
1109 }
1110
MeasureWidthLayoutCalPolicy(LayoutWrapper * layoutWrapper)1111 void TextLayoutAlgorithm::MeasureWidthLayoutCalPolicy(LayoutWrapper* layoutWrapper)
1112 {
1113 MeasureWithFixAtIdealSize(layoutWrapper);
1114 }
1115 } // namespace OHOS::Ace::NG
1116