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