1 /* 2 * Copyright (c) 2022-2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #include "core/components_ng/pattern/text/text_layout_algorithm.h" 17 18 #include <limits> 19 20 #include "base/geometry/dimension.h" 21 #include "base/log/ace_trace.h" 22 #include "base/utils/utils.h" 23 #include "core/components/common/layout/constants.h" 24 #include "core/components/common/properties/alignment.h" 25 #include "core/components/common/properties/text_style.h" 26 #include "core/components/hyperlink/hyperlink_theme.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/pattern/text_drag/text_drag_base.h" 32 #include "core/components_ng/render/font_collection.h" 33 #include "core/pipeline_ng/pipeline_context.h" 34 #include "core/text/text_emoji_processor.h" 35 36 namespace OHOS::Ace::NG { 37 TextLayoutAlgorithm(std::list<RefPtr<SpanItem>> spans,RefPtr<ParagraphManager> pManager,bool isSpanStringMode,bool isMarquee)38 TextLayoutAlgorithm::TextLayoutAlgorithm( 39 std::list<RefPtr<SpanItem>> spans, RefPtr<ParagraphManager> pManager, bool isSpanStringMode, bool isMarquee) 40 { 41 paragraphManager_ = pManager; 42 isSpanStringMode_ = isSpanStringMode; 43 44 if (!isSpanStringMode) { 45 if (!spans.empty()) { 46 spans_.emplace_back(std::move(spans)); 47 } 48 return; 49 } 50 if (spans.empty()) { 51 return; 52 } 53 if (isMarquee) { 54 for (const auto& span : spans) { 55 span->SetNeedRemoveNewLine(false); 56 } 57 spans_.emplace_back(std::move(spans)); 58 return; 59 } 60 ConstructParagraphSpanGroup(spans); 61 if (!spans.empty()) { 62 auto maxlines = spans.front()->textLineStyle->GetMaxLines().value_or(UINT32_MAX); 63 spanStringHasMaxLines_ |= maxlines != UINT32_MAX; 64 spans_.emplace_back(std::move(spans)); 65 } 66 } 67 68 TextLayoutAlgorithm::TextLayoutAlgorithm() = default; 69 ConstructParagraphSpanGroup(std::list<RefPtr<SpanItem>> & spans)70 void TextLayoutAlgorithm::ConstructParagraphSpanGroup(std::list<RefPtr<SpanItem>>& spans) 71 { 72 // split spans into groups by mew paragraph style 73 auto it = spans.begin(); 74 ParagraphStyle pStyle; 75 GetSpanParagraphStyle(nullptr, (*it), pStyle); 76 while (it != spans.end()) { 77 auto spanItem = *it; 78 if (!spanItem) { 79 ++it; 80 continue; 81 } 82 spanItem->SetNeedRemoveNewLine(false); 83 auto wContent = StringUtils::ToWstring(spanItem->content); 84 if (wContent.back() == L'\n') { 85 if (std::next(it) == spans.end()) { 86 break; 87 } 88 auto next = *(std::next(it)); 89 ParagraphStyle nextSpanParagraphStyle; 90 if (next) { 91 GetSpanParagraphStyle(nullptr, next, nextSpanParagraphStyle); 92 } else { 93 break; 94 } 95 if (pStyle != nextSpanParagraphStyle || 96 (pStyle.leadingMargin.has_value() && pStyle.leadingMargin->pixmap) || Positive(pStyle.indent.Value()) || 97 pStyle.maxLines != UINT32_MAX) { 98 std::list<RefPtr<SpanItem>> newGroup; 99 spanItem->SetNeedRemoveNewLine(true); 100 newGroup.splice(newGroup.begin(), spans, spans.begin(), std::next(it)); 101 spanStringHasMaxLines_ |= pStyle.maxLines != UINT32_MAX; 102 spans_.emplace_back(std::move(newGroup)); 103 it = spans.begin(); 104 pStyle = nextSpanParagraphStyle; 105 continue; 106 } 107 } 108 ++it; 109 } 110 } 111 OnReset()112 void TextLayoutAlgorithm::OnReset() {} 113 MeasureContent(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)114 std::optional<SizeF> TextLayoutAlgorithm::MeasureContent( 115 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper) 116 { 117 auto host = layoutWrapper->GetHostNode(); 118 CHECK_NULL_RETURN(host, std::nullopt); 119 ACE_SCOPED_TRACE("TextLayoutAlgorithm::MeasureContent[id:%d]", host->GetId()); 120 TextStyle textStyle; 121 ConstructTextStyles(contentConstraint, layoutWrapper, textStyle); 122 auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty()); 123 CHECK_NULL_RETURN(textLayoutProperty, std::nullopt); 124 if (textStyle.GetTextOverflow() == TextOverflow::MARQUEE) { // create a paragraph with all text in 1 line 125 isMarquee_ = true; 126 return BuildTextRaceParagraph(textStyle, textLayoutProperty, contentConstraint, layoutWrapper); 127 } 128 if (isSpanStringMode_) { 129 if (spanStringHasMaxLines_) { 130 textStyle.SetMaxLines(UINT32_MAX); 131 } 132 textStyle_ = textStyle; 133 BuildParagraph(textStyle, textLayoutProperty, contentConstraint, layoutWrapper); 134 } else { 135 textStyle_ = textStyle; 136 if (!AddPropertiesAndAnimations(textStyle, textLayoutProperty, contentConstraint, layoutWrapper)) { 137 return std::nullopt; 138 } 139 } 140 textStyle_ = textStyle; 141 baselineOffset_ = textStyle.GetBaselineOffset().ConvertToPxDistribute( 142 textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale()); 143 if (NearZero(contentConstraint.maxSize.Height()) || NearZero(contentConstraint.maxSize.Width())) { 144 return SizeF {}; 145 } 146 CHECK_NULL_RETURN(paragraphManager_, std::nullopt); 147 auto height = paragraphManager_->GetHeight(); 148 auto maxWidth = paragraphManager_->GetMaxWidth(); 149 auto longestLine = paragraphManager_->GetLongestLine(); 150 auto heightFinal = static_cast<float>(height + std::fabs(baselineOffset_)); 151 if (contentConstraint.selfIdealSize.Height().has_value()) { 152 heightFinal = std::min(heightFinal, contentConstraint.selfIdealSize.Height().value()); 153 } else { 154 heightFinal = std::min(heightFinal, contentConstraint.maxSize.Height()); 155 } 156 if (SystemProperties::GetDebugEnabled()) { 157 TAG_LOGI(AceLogTag::ACE_TEXT, "MeasureContent[id:%d][w:%f][h:%f][lineW:%f][height:%f]", host->GetId(), maxWidth, 158 heightFinal, longestLine, height); 159 } 160 if (host->GetTag() == V2::TEXT_ETS_TAG && textLayoutProperty->GetContent().value_or("").empty() && 161 NonPositive(longestLine)) { 162 ACE_SCOPED_TRACE("TextHeightFinal [%f], TextContentWidth [%f], FontSize [%lf]", heightFinal, maxWidth, 163 textStyle.GetFontSize().ConvertToPxDistribute( 164 textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale())); 165 return SizeF {}; 166 } 167 return SizeF(maxWidth, heightFinal); 168 } 169 AddPropertiesAndAnimations(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & textLayoutProperty,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)170 bool TextLayoutAlgorithm::AddPropertiesAndAnimations(TextStyle& textStyle, 171 const RefPtr<TextLayoutProperty>& textLayoutProperty, const LayoutConstraintF& contentConstraint, 172 LayoutWrapper* layoutWrapper) 173 { 174 bool result = false; 175 switch (textLayoutProperty->GetHeightAdaptivePolicyValue(TextHeightAdaptivePolicy::MAX_LINES_FIRST)) { 176 case TextHeightAdaptivePolicy::MAX_LINES_FIRST: 177 result = BuildParagraph(textStyle, textLayoutProperty, contentConstraint, layoutWrapper); 178 break; 179 case TextHeightAdaptivePolicy::MIN_FONT_SIZE_FIRST: 180 result = BuildParagraphAdaptUseMinFontSize(textStyle, textLayoutProperty, contentConstraint, layoutWrapper); 181 break; 182 case TextHeightAdaptivePolicy::LAYOUT_CONSTRAINT_FIRST: 183 result = 184 BuildParagraphAdaptUseLayoutConstraint(textStyle, textLayoutProperty, contentConstraint, layoutWrapper); 185 break; 186 default: 187 break; 188 } 189 return result; 190 } 191 UpdateParagraphForAISpan(const TextStyle & textStyle,LayoutWrapper * layoutWrapper,const RefPtr<Paragraph> & paragraph)192 void TextLayoutAlgorithm::UpdateParagraphForAISpan( 193 const TextStyle& textStyle, LayoutWrapper* layoutWrapper, const RefPtr<Paragraph>& paragraph) 194 { 195 CHECK_NULL_VOID(layoutWrapper); 196 auto layoutProperty = layoutWrapper->GetLayoutProperty(); 197 CHECK_NULL_VOID(layoutProperty); 198 auto frameNode = layoutWrapper->GetHostNode(); 199 CHECK_NULL_VOID(frameNode); 200 auto pattern = frameNode->GetPattern<TextPattern>(); 201 CHECK_NULL_VOID(pattern); 202 auto textForAI = pattern->GetTextForAI(); 203 auto wTextForAI = StringUtils::ToWstring(textForAI); 204 int32_t wTextForAILength = static_cast<int32_t>(wTextForAI.length()); 205 int32_t preEnd = 0; 206 DragSpanPosition dragSpanPosition; 207 dragSpanPosition.dragStart = pattern->GetRecoverStart(); 208 dragSpanPosition.dragEnd = pattern->GetRecoverEnd(); 209 bool isDragging = pattern->IsDragging(); 210 TextStyle aiSpanStyle = textStyle; 211 pattern->ModifyAISpanStyle(aiSpanStyle); 212 for (auto kv : pattern->GetAISpanMap()) { 213 if (preEnd >= wTextForAILength) { 214 break; 215 } 216 auto aiSpan = kv.second; 217 if (aiSpan.start < preEnd) { 218 TAG_LOGI(AceLogTag::ACE_TEXT, "Error prediction"); 219 continue; 220 } 221 if (preEnd < aiSpan.start) { 222 dragSpanPosition.spanStart = preEnd; 223 dragSpanPosition.spanEnd = aiSpan.start; 224 GrayDisplayAISpan(dragSpanPosition, wTextForAI, textStyle, isDragging, paragraph); 225 } 226 preEnd = aiSpan.end; 227 dragSpanPosition.spanStart = aiSpan.start; 228 dragSpanPosition.spanEnd = aiSpan.end; 229 GrayDisplayAISpan(dragSpanPosition, wTextForAI, aiSpanStyle, isDragging, paragraph); 230 } 231 if (preEnd < wTextForAILength) { 232 dragSpanPosition.spanStart = preEnd; 233 dragSpanPosition.spanEnd = wTextForAILength; 234 GrayDisplayAISpan(dragSpanPosition, wTextForAI, textStyle, isDragging, paragraph); 235 } 236 } 237 GrayDisplayAISpan(const DragSpanPosition & dragSpanPosition,const std::wstring wTextForAI,const TextStyle & textStyle,bool isDragging,const RefPtr<Paragraph> & paragraph)238 void TextLayoutAlgorithm::GrayDisplayAISpan(const DragSpanPosition& dragSpanPosition, const std::wstring wTextForAI, 239 const TextStyle& textStyle, bool isDragging, const RefPtr<Paragraph>& paragraph) 240 { 241 int32_t dragStart = dragSpanPosition.dragStart; 242 int32_t dragEnd = dragSpanPosition.dragEnd; 243 int32_t spanStart = dragSpanPosition.spanStart; 244 int32_t spanEnd = dragSpanPosition.spanEnd; 245 std::vector<std::string> contents = {}; 246 std::string firstParagraph = ""; 247 std::string secondParagraph = ""; 248 std::string thirdParagraph = ""; 249 if (dragStart > spanEnd || dragEnd < spanStart || !isDragging) { 250 firstParagraph = StringOutBoundProtection(spanStart, spanEnd - spanStart, wTextForAI); 251 } else if (spanStart <= dragStart && spanEnd >= dragStart && spanEnd <= dragEnd) { 252 firstParagraph = StringOutBoundProtection(spanStart, dragStart - spanStart, wTextForAI); 253 secondParagraph = StringOutBoundProtection(dragStart, spanEnd - dragStart, wTextForAI); 254 } else if (spanStart >= dragStart && spanEnd <= dragEnd) { 255 secondParagraph = StringOutBoundProtection(spanStart, spanEnd - spanStart, wTextForAI); 256 } else if (spanStart <= dragStart && spanEnd >= dragEnd) { 257 firstParagraph = StringOutBoundProtection(spanStart, dragStart - spanStart, wTextForAI); 258 secondParagraph = StringOutBoundProtection(dragStart, dragEnd - dragStart, wTextForAI); 259 thirdParagraph = StringOutBoundProtection(dragEnd, spanEnd - dragEnd, wTextForAI); 260 } else { 261 secondParagraph = StringOutBoundProtection(spanStart, dragEnd - spanStart, wTextForAI); 262 thirdParagraph = StringOutBoundProtection(dragEnd, spanEnd - dragEnd, wTextForAI); 263 } 264 contents = { firstParagraph, secondParagraph, thirdParagraph }; 265 CreateParagraphDrag(textStyle, contents, paragraph); 266 } 267 StringOutBoundProtection(int32_t position,int32_t length,std::wstring wTextForAI)268 std::string TextLayoutAlgorithm::StringOutBoundProtection(int32_t position, int32_t length, std::wstring wTextForAI) 269 { 270 int32_t wTextForAILength = static_cast<int32_t>(wTextForAI.length()); 271 if (position >= wTextForAILength || length > wTextForAILength - position) { 272 return ""; 273 } else { 274 return StringUtils::ToString(wTextForAI.substr(position, length)); 275 } 276 } 277 CreateParagraph(const TextStyle & textStyle,std::string content,LayoutWrapper * layoutWrapper,double maxWidth)278 bool TextLayoutAlgorithm::CreateParagraph( 279 const TextStyle& textStyle, std::string content, LayoutWrapper* layoutWrapper, double maxWidth) 280 { 281 if (!paragraphManager_) { 282 paragraphManager_ = AceType::MakeRefPtr<ParagraphManager>(); 283 } 284 paragraphManager_->Reset(); 285 auto frameNode = layoutWrapper->GetHostNode(); 286 CHECK_NULL_RETURN(frameNode, false); 287 auto pattern = frameNode->GetPattern<TextPattern>(); 288 CHECK_NULL_RETURN(pattern, false); 289 pattern->ClearCustomSpanPlaceholderInfo(); 290 if (pattern->IsSensitiveEnalbe()) { 291 UpdateSensitiveContent(content); 292 } 293 auto useExternalParagraph = pattern->GetExternalParagraph() && !pattern->NeedShowAIDetect(); 294 auto externalParagraphStyle = pattern->GetExternalParagraphStyle(); 295 auto paraStyle = GetParagraphStyle(textStyle, content, layoutWrapper); 296 if (pattern->GetExternalParagraph()) { 297 if (!useExternalParagraph && externalParagraphStyle) { 298 paraStyle = externalParagraphStyle.value(); 299 } 300 } 301 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) || isSpanStringMode_) { 302 paraStyle.fontSize = textStyle.GetFontSize().ConvertToPxDistribute( 303 textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale()); 304 } 305 paraStyle.leadingMarginAlign = Alignment::CENTER; 306 // SymbolGlyph 307 if (frameNode->GetTag() == V2::SYMBOL_ETS_TAG) { 308 return UpdateSymbolTextStyle(textStyle, paraStyle, layoutWrapper, frameNode); 309 } 310 if (spans_.empty() || useExternalParagraph) { 311 // only use for text. 312 return UpdateSingleParagraph(layoutWrapper, paraStyle, textStyle, content, maxWidth); 313 } else { 314 return UpdateParagraphBySpan(layoutWrapper, paraStyle, maxWidth, textStyle); 315 } 316 } 317 UpdateSymbolTextStyle(const TextStyle & textStyle,const ParagraphStyle & paraStyle,LayoutWrapper * layoutWrapper,RefPtr<FrameNode> & frameNode)318 bool TextLayoutAlgorithm::UpdateSymbolTextStyle(const TextStyle& textStyle, const ParagraphStyle& paraStyle, 319 LayoutWrapper* layoutWrapper, RefPtr<FrameNode>& frameNode) 320 { 321 auto &¶graph = Paragraph::Create(paraStyle, FontCollection::Current()); 322 CHECK_NULL_RETURN(paragraph, false); 323 auto layoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty()); 324 CHECK_NULL_RETURN(layoutProperty, false); 325 auto symbolSourceInfo = layoutProperty->GetSymbolSourceInfo(); 326 CHECK_NULL_RETURN(symbolSourceInfo, false); 327 TextStyle symbolTextStyle = textStyle; 328 symbolTextStyle.isSymbolGlyph_ = true; 329 symbolTextStyle.SetRenderStrategy( 330 symbolTextStyle.GetRenderStrategy() < 0 ? 0 : symbolTextStyle.GetRenderStrategy()); 331 symbolTextStyle.SetEffectStrategy( 332 symbolTextStyle.GetEffectStrategy() < 0 ? 0 : symbolTextStyle.GetEffectStrategy()); 333 symbolTextStyle.SetFontFamilies({ "HM Symbol" }); 334 paragraph->PushStyle(symbolTextStyle); 335 if (symbolTextStyle.GetSymbolEffectOptions().has_value()) { 336 auto symbolEffectOptions = layoutProperty->GetSymbolEffectOptionsValue(SymbolEffectOptions()); 337 symbolEffectOptions.Reset(); 338 layoutProperty->UpdateSymbolEffectOptions(symbolEffectOptions); 339 if (symbolTextStyle.GetSymbolEffectOptions().has_value()) { 340 auto symboloptiOns = symbolTextStyle.GetSymbolEffectOptions().value(); 341 symboloptiOns.Reset(); 342 } 343 } 344 paragraph->AddSymbol(symbolSourceInfo->GetUnicode()); 345 paragraph->PopStyle(); 346 paragraph->Build(); 347 paragraph->SetParagraphSymbolAnimation(frameNode); 348 paragraphManager_->AddParagraph({ .paragraph = paragraph, .paragraphStyle = paraStyle, .start = 0, .end = 2 }); 349 return true; 350 } 351 CreateParagraphDrag(const TextStyle & textStyle,const std::vector<std::string> & contents,const RefPtr<Paragraph> & paragraph)352 void TextLayoutAlgorithm::CreateParagraphDrag( 353 const TextStyle& textStyle, const std::vector<std::string>& contents, const RefPtr<Paragraph>& paragraph) 354 { 355 TextStyle dragTextStyle = textStyle; 356 Color color = textStyle.GetTextColor().ChangeAlpha(DRAGGED_TEXT_TRANSPARENCY); 357 dragTextStyle.SetTextColor(color); 358 Color textDecorationColor = textStyle.GetTextDecorationColor().ChangeAlpha(DRAGGED_TEXT_TRANSPARENCY); 359 dragTextStyle.SetTextDecorationColor(textDecorationColor); 360 std::vector<TextStyle> textStyles { textStyle, dragTextStyle, textStyle }; 361 362 CHECK_NULL_VOID(paragraph); 363 for (size_t i = 0; i < contents.size(); i++) { 364 std::string splitStr = contents[i]; 365 if (splitStr.empty()) { 366 continue; 367 } 368 auto& style = textStyles[i]; 369 paragraph->PushStyle(style); 370 StringUtils::TransformStrCase(splitStr, static_cast<int32_t>(style.GetTextCase())); 371 paragraph->AddText(StringUtils::Str8ToStr16(splitStr)); 372 paragraph->PopStyle(); 373 } 374 } 375 CreateParagraphAndLayout(const TextStyle & textStyle,const std::string & content,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper,bool needLayout)376 bool TextLayoutAlgorithm::CreateParagraphAndLayout(const TextStyle& textStyle, const std::string& content, 377 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper, bool needLayout) 378 { 379 auto maxSize = MultipleParagraphLayoutAlgorithm::GetMaxMeasureSize(contentConstraint); 380 ACE_TEXT_SCOPED_TRACE("CreateParagraphAndLayout[maxSize:%s][Len:%d]", maxSize.ToString().c_str(), 381 static_cast<int32_t>(content.length())); 382 if (!CreateParagraph(textStyle, content, layoutWrapper, maxSize.Width())) { 383 return false; 384 } 385 CHECK_NULL_RETURN(paragraphManager_, false); 386 auto paragraphInfo = paragraphManager_->GetParagraphs(); 387 for (auto pIter = paragraphInfo.begin(); pIter != paragraphInfo.end(); pIter++) { 388 auto paragraph = pIter->paragraph; 389 CHECK_NULL_RETURN(paragraph, false); 390 paragraph->Layout(maxSize.Width()); 391 } 392 return true; 393 } 394 GetContentOffset(LayoutWrapper * layoutWrapper)395 OffsetF TextLayoutAlgorithm::GetContentOffset(LayoutWrapper* layoutWrapper) 396 { 397 return SetContentOffset(layoutWrapper); 398 } 399 AdaptMinTextSize(TextStyle & textStyle,const std::string & content,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)400 bool TextLayoutAlgorithm::AdaptMinTextSize(TextStyle& textStyle, const std::string& content, 401 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper) 402 { 403 ACE_TEXT_SCOPED_TRACE("TextLayoutAlgorithm::AdaptMinTextSize[Length:%d]", static_cast<int32_t>(content.length())); 404 double maxFontSize = 0.0; 405 double minFontSize = 0.0; 406 auto pipeline = PipelineContext::GetCurrentContextSafely(); 407 CHECK_NULL_RETURN(pipeline, false); 408 auto maxFontSizeDimension = textStyle.GetAdaptMaxFontSize(); 409 maxFontSize = maxFontSizeDimension.ConvertToPxDistribute( 410 textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale()); 411 minFontSize = textStyle.GetAdaptMinFontSize().ConvertToPxDistribute( 412 textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale()); 413 if (LessNotEqual(maxFontSize, minFontSize) || LessOrEqual(minFontSize, 0.0)) { 414 if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) { 415 TAG_LOGE(AceLogTag::ACE_TEXT, "create paragraph error"); 416 return false; 417 } 418 return true; 419 } 420 auto adaptUnit = maxFontSizeDimension.GetAdaptDimensionUnit(textStyle.GetAdaptMinFontSize()); 421 Dimension step(1.0f, adaptUnit); 422 if (GreatNotEqual(textStyle.GetAdaptFontSizeStep().Value(), 0.0)) { 423 step = textStyle.GetAdaptFontSizeStep(); 424 } 425 double stepSize = 426 step.ConvertToPxDistribute(textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale()); 427 auto maxSize = MultipleParagraphLayoutAlgorithm::GetMaxMeasureSize(contentConstraint); 428 while (GreatOrEqual(maxFontSize, minFontSize)) { 429 textStyle.SetFontSize(Dimension(maxFontSize)); 430 if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) { 431 TAG_LOGE(AceLogTag::ACE_TEXT, "create paragraph error"); 432 return false; 433 } 434 if (!DidExceedMaxLines(maxSize)) { 435 break; 436 } 437 bool isEqual = maxFontSize == minFontSize; 438 maxFontSize -= stepSize; 439 if (LessNotEqual(maxFontSize, minFontSize) && !isEqual) { 440 maxFontSize = minFontSize; 441 } 442 } 443 return true; 444 } 445 GetBaselineOffset() const446 float TextLayoutAlgorithm::GetBaselineOffset() const 447 { 448 return baselineOffset_; 449 } 450 UpdateSingleParagraph(LayoutWrapper * layoutWrapper,ParagraphStyle paraStyle,const TextStyle & textStyle,const std::string & content,double maxWidth)451 bool TextLayoutAlgorithm::UpdateSingleParagraph(LayoutWrapper* layoutWrapper, ParagraphStyle paraStyle, 452 const TextStyle& textStyle, const std::string& content, double maxWidth) 453 { 454 auto host = layoutWrapper->GetHostNode(); 455 CHECK_NULL_RETURN(host, false); 456 ACE_TEXT_SCOPED_TRACE("TextLayoutAlgorithm::UpdateSingleParagraph[id:%d][length:%d][w:%f]", host->GetId(), 457 static_cast<int32_t>(content.length()), maxWidth); 458 auto pattern = host->GetPattern<TextPattern>(); 459 CHECK_NULL_RETURN(pattern, false); 460 auto externalParagraph = pattern->GetExternalParagraph(); 461 RefPtr<Paragraph> paragraph; 462 if (externalParagraph) { 463 paragraph = Paragraph::Create(externalParagraph.value()); 464 } else { 465 paragraph = Paragraph::Create(paraStyle, FontCollection::Current()); 466 } 467 CHECK_NULL_RETURN(paragraph, false); 468 auto textStyleTmp = textStyle; 469 textStyleTmp.ResetTextBaseline(); 470 paragraph->PushStyle(textStyleTmp); 471 if (pattern->NeedShowAIDetect()) { 472 UpdateParagraphForAISpan(textStyle, layoutWrapper, paragraph); 473 } else { 474 if (pattern->IsDragging()) { 475 auto dragContents = pattern->GetDragContents(); 476 CreateParagraphDrag(textStyle, dragContents, paragraph); 477 } else { 478 auto value = content; 479 StringUtils::TransformStrCase(value, static_cast<int32_t>(textStyle.GetTextCase())); 480 std::u16string result = StringUtils::Str8ToStr16(value); 481 if (result.length() == 0 && value.length() != 0) { 482 value = TextEmojiProcessor::ConvertU8stringUnpairedSurrogates(value); 483 result = StringUtils::Str8ToStr16(value); 484 } 485 paragraph->AddText(result); 486 } 487 } 488 paragraph->Build(); 489 ApplyIndent(paraStyle, paragraph, maxWidth, textStyle); 490 paragraphManager_->AddParagraph({ .paragraph = paragraph, 491 .paragraphStyle = paraStyle, 492 .start = 0, 493 .end = StringUtils::Str8ToStr16(content).length() }); 494 return true; 495 } 496 BuildParagraph(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)497 bool TextLayoutAlgorithm::BuildParagraph(TextStyle& textStyle, const RefPtr<TextLayoutProperty>& layoutProperty, 498 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper) 499 { 500 auto host = layoutWrapper->GetHostNode(); 501 CHECK_NULL_RETURN(host, false); 502 auto pattern = host->GetPattern<TextPattern>(); 503 CHECK_NULL_RETURN(pattern, false); 504 pattern->DumpRecord(",BuildParagraph id:" + std::to_string(host->GetId())); 505 if (!textStyle.GetAdaptTextSize() || !spans_.empty()) { 506 if (!CreateParagraphAndLayout( 507 textStyle, layoutProperty->GetContent().value_or(""), contentConstraint, layoutWrapper)) { 508 TAG_LOGE(AceLogTag::ACE_TEXT, "create paragraph error"); 509 return false; 510 } 511 } else { 512 if (!AdaptMinTextSize(textStyle, layoutProperty->GetContent().value_or(""), contentConstraint, layoutWrapper)) { 513 return false; 514 } 515 } 516 return ParagraphReLayout(contentConstraint); 517 } 518 BuildParagraphAdaptUseMinFontSize(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)519 bool TextLayoutAlgorithm::BuildParagraphAdaptUseMinFontSize(TextStyle& textStyle, 520 const RefPtr<TextLayoutProperty>& layoutProperty, const LayoutConstraintF& contentConstraint, 521 LayoutWrapper* layoutWrapper) 522 { 523 if (!AdaptMaxTextSize(textStyle, layoutProperty->GetContent().value_or(""), contentConstraint, layoutWrapper)) { 524 return false; 525 } 526 auto paragraph = GetSingleParagraph(); 527 CHECK_NULL_RETURN(paragraph, false); 528 // Confirmed specification: The width of the text paragraph covers the width of the component, so this code is 529 // generally not allowed to be modified 530 if (!contentConstraint.selfIdealSize.Width()) { 531 float paragraphNewWidth = std::min(std::min(paragraph->GetTextWidth(), paragraph->GetMaxWidth()), 532 MultipleParagraphLayoutAlgorithm::GetMaxMeasureSize(contentConstraint).Width()); 533 paragraphNewWidth = 534 std::clamp(paragraphNewWidth, contentConstraint.minSize.Width(), contentConstraint.maxSize.Width()); 535 if (!NearEqual(paragraphNewWidth, paragraph->GetMaxWidth())) { 536 paragraph->Layout(std::ceil(paragraphNewWidth)); 537 } 538 } 539 540 return true; 541 } 542 BuildParagraphAdaptUseLayoutConstraint(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)543 bool TextLayoutAlgorithm::BuildParagraphAdaptUseLayoutConstraint(TextStyle& textStyle, 544 const RefPtr<TextLayoutProperty>& layoutProperty, const LayoutConstraintF& contentConstraint, 545 LayoutWrapper* layoutWrapper) 546 { 547 // Create the paragraph and obtain the height. 548 if (!BuildParagraph(textStyle, layoutProperty, contentConstraint, layoutWrapper)) { 549 return false; 550 } 551 552 auto paragraph = GetSingleParagraph(); 553 CHECK_NULL_RETURN(paragraph, false); 554 auto lineCount = static_cast<uint32_t>(paragraph->GetLineCount()); 555 lineCount = std::max(std::min(textStyle.GetMaxLines(), lineCount), static_cast<uint32_t>(0)); 556 textStyle.SetMaxLines(lineCount); 557 textStyle.DisableAdaptTextSize(); 558 559 auto height = static_cast<float>(paragraph->GetHeight()); 560 while (GreatNotEqual(height, contentConstraint.maxSize.Height())) { 561 auto maxLines = textStyle.GetMaxLines(); 562 if (maxLines == 0) { 563 break; 564 } else { 565 maxLines = textStyle.GetMaxLines() - 1; 566 textStyle.SetMaxLines(maxLines); 567 } 568 if (!BuildParagraph(textStyle, layoutProperty, contentConstraint, layoutWrapper)) { 569 return false; 570 } 571 paragraph = GetSingleParagraph(); 572 height = static_cast<float>(paragraph->GetHeight()); 573 } 574 return true; 575 } 576 BuildTextRaceParagraph(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)577 std::optional<SizeF> TextLayoutAlgorithm::BuildTextRaceParagraph(TextStyle& textStyle, 578 const RefPtr<TextLayoutProperty>& layoutProperty, const LayoutConstraintF& contentConstraint, 579 LayoutWrapper* layoutWrapper) 580 { 581 // create a paragraph with all text in 1 line 582 textStyle.SetTextOverflow(TextOverflow::CLIP); 583 textStyle.SetMaxLines(1); 584 textStyle.SetTextIndent(Dimension(0.0f)); 585 std::string content = layoutProperty->GetContent().value_or(""); 586 std::replace(content.begin(), content.end(), '\n', ' '); 587 if (!textStyle.GetAdaptTextSize()) { 588 if (!CreateParagraph(textStyle, content, layoutWrapper)) { 589 return std::nullopt; 590 } 591 } else { 592 if (!AdaptMinTextSize(textStyle, content, contentConstraint, layoutWrapper)) { 593 return std::nullopt; 594 } 595 } 596 597 textStyle_ = textStyle; 598 auto paragraph = GetSingleParagraph(); 599 // layout the paragraph to the width of text 600 paragraph->Layout(std::numeric_limits<float>::max()); 601 float paragraphWidth = paragraph->GetLongestLineWithIndent(); 602 if (contentConstraint.selfIdealSize.Width().has_value()) { 603 paragraphWidth = std::max(contentConstraint.selfIdealSize.Width().value(), paragraphWidth); 604 } else { 605 paragraphWidth = std::max(contentConstraint.minSize.Width(), paragraphWidth); 606 } 607 paragraphWidth = std::ceil(paragraphWidth); 608 paragraph->Layout(paragraphWidth); 609 610 auto pipeline = PipelineContext::GetCurrentContextSafely(); 611 // calculate the content size 612 auto height = static_cast<float>(paragraph->GetHeight()); 613 baselineOffset_ = static_cast<float>( 614 layoutProperty->GetBaselineOffsetValue(Dimension()) 615 .ConvertToPxDistribute(textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale())); 616 float heightFinal = 617 std::min(static_cast<float>(height + std::fabs(baselineOffset_)), contentConstraint.maxSize.Height()); 618 619 float widthFinal = paragraphWidth; 620 if (contentConstraint.selfIdealSize.Width().has_value()) { 621 if (contentConstraint.selfIdealSize.Width().value() < paragraphWidth) { 622 widthFinal = contentConstraint.selfIdealSize.Width().value(); 623 } 624 } else if (contentConstraint.maxSize.Width() < paragraphWidth) { 625 widthFinal = contentConstraint.maxSize.Width(); 626 } 627 return SizeF(widthFinal, heightFinal); 628 } 629 AdaptMaxTextSize(TextStyle & textStyle,const std::string & content,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)630 bool TextLayoutAlgorithm::AdaptMaxTextSize(TextStyle& textStyle, const std::string& content, 631 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper) 632 { 633 constexpr Dimension ADAPT_UNIT = 1.0_fp; 634 auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty()); 635 CHECK_NULL_RETURN(textLayoutProperty, false); 636 auto step = textLayoutProperty->GetAdaptFontSizeStepValue(ADAPT_UNIT); 637 return AdaptMaxFontSize(textStyle, content, step, contentConstraint, layoutWrapper); 638 } 639 UpdateSensitiveContent(std::string & content)640 void TextLayoutAlgorithm::UpdateSensitiveContent(std::string& content) 641 { 642 auto wContent = StringUtils::ToWstring(content); 643 std::replace_if( 644 wContent.begin(), wContent.end(), 645 [](wchar_t ch) { 646 return ch != L'\n'; 647 }, L'-'); 648 content = StringUtils::ToString(wContent); 649 } 650 GetTextStyle() const651 std::optional<TextStyle> TextLayoutAlgorithm::GetTextStyle() const 652 { 653 return textStyle_; 654 } 655 GetLineCount() const656 size_t TextLayoutAlgorithm::GetLineCount() const 657 { 658 size_t count = 0; 659 CHECK_NULL_RETURN(paragraphManager_, 0); 660 auto paragraphInfo = paragraphManager_->GetParagraphs(); 661 for (auto pIter = paragraphInfo.begin(); pIter != paragraphInfo.end(); pIter++) { 662 auto paragraph = pIter->paragraph; 663 CHECK_NULL_RETURN(paragraph, 0); 664 count += paragraph->GetLineCount(); 665 } 666 return count; 667 } 668 } // namespace OHOS::Ace::NG 669