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 "text_layout_adapter.h"
21
22 #include "base/geometry/dimension.h"
23 #include "base/i18n/localization.h"
24 #include "base/utils/utils.h"
25 #include "core/common/container.h"
26 #include "core/common/font_manager.h"
27 #include "core/components/text/text_theme.h"
28 #include "core/components_ng/base/frame_node.h"
29 #include "core/components_ng/pattern/image/image_layout_property.h"
30 #include "core/components_ng/pattern/text/text_layout_property.h"
31 #include "core/components_ng/pattern/text/text_pattern.h"
32 #include "core/components_ng/render/drawing_prop_convertor.h"
33 #include "core/components_ng/render/font_collection.h"
34 #include "core/pipeline_ng/pipeline_context.h"
35
36 namespace OHOS::Ace::NG {
37 namespace {
38 constexpr int32_t SYMBOL_SPAN_LENGTH = 2;
39 /**
40 * The baseline information needs to be calculated based on contentOffsetY.
41 */
GetContentOffsetY(LayoutWrapper * layoutWrapper)42 float GetContentOffsetY(LayoutWrapper* layoutWrapper)
43 {
44 auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
45 const auto& padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
46 auto offsetY = padding.top.value_or(0);
47 auto align = Alignment::CENTER;
48 if (layoutWrapper->GetLayoutProperty()->GetPositionProperty()) {
49 align = layoutWrapper->GetLayoutProperty()->GetPositionProperty()->GetAlignment().value_or(align);
50 }
51 const auto& content = layoutWrapper->GetGeometryNode()->GetContent();
52 if (content) {
53 offsetY += Alignment::GetAlignPosition(size, content->GetRect().GetSize(), align).GetY();
54 }
55 return offsetY;
56 }
57 } // namespace
58
59 TextLayoutAlgorithm::TextLayoutAlgorithm() = default;
60
OnReset()61 void TextLayoutAlgorithm::OnReset() {}
62
MeasureContent(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)63 std::optional<SizeF> TextLayoutAlgorithm::MeasureContent(
64 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
65 {
66 if (Negative(contentConstraint.maxSize.Width()) || Negative(contentConstraint.maxSize.Height())) {
67 return std::nullopt;
68 }
69
70 auto frameNode = layoutWrapper->GetHostNode();
71 CHECK_NULL_RETURN(frameNode, std::nullopt);
72 auto pipeline = frameNode->GetContext();
73 CHECK_NULL_RETURN(pipeline, std::nullopt);
74 auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
75 CHECK_NULL_RETURN(textLayoutProperty, std::nullopt);
76 auto pattern = frameNode->GetPattern<TextPattern>();
77 CHECK_NULL_RETURN(pattern, std::nullopt);
78 auto contentModifier = pattern->GetContentModifier();
79
80 TextStyle textStyle = CreateTextStyleUsingTheme(
81 textLayoutProperty->GetFontStyle(), textLayoutProperty->GetTextLineStyle(), pipeline->GetTheme<TextTheme>());
82 if (contentModifier) {
83 SetPropertyToModifier(textLayoutProperty, contentModifier);
84 contentModifier->ModifyTextStyle(textStyle);
85 contentModifier->SetFontReady(false);
86 }
87 textStyle.SetHalfLeading(pipeline->GetHalfLeading());
88 // Register callback for fonts.
89 FontRegisterCallback(frameNode, textStyle);
90
91 // Determines whether a foreground color is set or inherited.
92 UpdateTextColorIfForeground(frameNode, textStyle);
93
94 if (textStyle.GetTextOverflow() == TextOverflow::MARQUEE) {
95 return BuildTextRaceParagraph(textStyle, textLayoutProperty, contentConstraint, pipeline, layoutWrapper);
96 }
97
98 if (!AddPropertiesAndAnimations(textStyle, textLayoutProperty, contentConstraint, pipeline, layoutWrapper)) {
99 return std::nullopt;
100 }
101
102 textStyle_ = textStyle;
103
104 auto height = static_cast<float>(paragraph_->GetHeight());
105 double baselineOffset = 0.0;
106 textStyle.GetBaselineOffset().NormalizeToPx(
107 pipeline->GetDipScale(), pipeline->GetFontScale(), pipeline->GetLogicScale(), 0.0f, baselineOffset);
108
109 baselineOffset_ = static_cast<float>(baselineOffset);
110 auto heightFinal = static_cast<float>(height + std::fabs(baselineOffset));
111 if (contentConstraint.selfIdealSize.Height().has_value()) {
112 heightFinal = std::min(
113 static_cast<float>(height + std::fabs(baselineOffset)), contentConstraint.selfIdealSize.Height().value());
114 } else {
115 heightFinal =
116 std::min(static_cast<float>(height + std::fabs(baselineOffset)), contentConstraint.maxSize.Height());
117 }
118 if (NearZero(contentConstraint.maxSize.Height()) || NearZero(contentConstraint.maxSize.Width())) {
119 return SizeF {};
120 }
121 if (frameNode->GetTag() == V2::TEXT_ETS_TAG && textLayoutProperty->GetContent().value_or("").empty() &&
122 NonPositive(static_cast<double>(paragraph_->GetLongestLine()))) {
123 // text content is empty
124 ACE_SCOPED_TRACE("TextHeightFinal [%f], TextContentWidth [%f], FontSize [%lf]",
125 heightFinal, paragraph_->GetMaxWidth(), textStyle.GetFontSize().ConvertToPx());
126 return SizeF {};
127 }
128 return SizeF(paragraph_->GetMaxWidth(), heightFinal);
129 }
130
AddPropertiesAndAnimations(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & textLayoutProperty,const LayoutConstraintF & contentConstraint,const RefPtr<PipelineContext> & pipeline,LayoutWrapper * layoutWrapper)131 bool TextLayoutAlgorithm::AddPropertiesAndAnimations(TextStyle& textStyle,
132 const RefPtr<TextLayoutProperty>& textLayoutProperty, const LayoutConstraintF& contentConstraint,
133 const RefPtr<PipelineContext>& pipeline, LayoutWrapper* layoutWrapper)
134 {
135 bool result = false;
136 switch (textLayoutProperty->GetHeightAdaptivePolicyValue(TextHeightAdaptivePolicy::MAX_LINES_FIRST)) {
137 case TextHeightAdaptivePolicy::MAX_LINES_FIRST:
138 result = BuildParagraph(textStyle, textLayoutProperty, contentConstraint, pipeline, layoutWrapper);
139 break;
140 case TextHeightAdaptivePolicy::MIN_FONT_SIZE_FIRST:
141 result = BuildParagraphAdaptUseMinFontSize(
142 textStyle, textLayoutProperty, contentConstraint, pipeline, layoutWrapper);
143 break;
144 case TextHeightAdaptivePolicy::LAYOUT_CONSTRAINT_FIRST:
145 result = BuildParagraphAdaptUseLayoutConstraint(
146 textStyle, textLayoutProperty, contentConstraint, pipeline, layoutWrapper);
147 break;
148 default:
149 break;
150 }
151 return result;
152 }
153
FontRegisterCallback(const RefPtr<FrameNode> & frameNode,const TextStyle & textStyle)154 void TextLayoutAlgorithm::FontRegisterCallback(const RefPtr<FrameNode>& frameNode, const TextStyle& textStyle)
155 {
156 auto callback = [weakNode = WeakPtr<FrameNode>(frameNode)] {
157 auto frameNode = weakNode.Upgrade();
158 CHECK_NULL_VOID(frameNode);
159 frameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
160 auto pattern = frameNode->GetPattern<TextPattern>();
161 CHECK_NULL_VOID(pattern);
162 auto modifier = DynamicCast<TextContentModifier>(pattern->GetContentModifier());
163 CHECK_NULL_VOID(modifier);
164 modifier->SetFontReady(true);
165 };
166 auto pipeline = frameNode->GetContext();
167 CHECK_NULL_VOID(pipeline);
168 auto fontManager = pipeline->GetFontManager();
169 if (fontManager) {
170 bool isCustomFont = false;
171 for (const auto& familyName : textStyle.GetFontFamilies()) {
172 bool customFont = fontManager->RegisterCallbackNG(frameNode, familyName, callback);
173 if (customFont) {
174 isCustomFont = true;
175 }
176 }
177 fontManager->AddVariationNodeNG(frameNode);
178 if (isCustomFont || fontManager->IsDefaultFontChanged()) {
179 auto pattern = frameNode->GetPattern<TextPattern>();
180 CHECK_NULL_VOID(pattern);
181 pattern->SetIsCustomFont(true);
182 auto modifier = DynamicCast<TextContentModifier>(pattern->GetContentModifier());
183 CHECK_NULL_VOID(modifier);
184 modifier->SetIsCustomFont(true);
185 }
186 }
187 }
188
Measure(LayoutWrapper * layoutWrapper)189 void TextLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
190 {
191 BoxLayoutAlgorithm::Measure(layoutWrapper);
192 auto baselineDistance = 0.0f;
193 if (paragraph_) {
194 baselineDistance = paragraph_->GetAlphabeticBaseline() + std::max(GetBaselineOffset(), 0.0f);
195 }
196 if (!NearZero(baselineDistance, 0.0f)) {
197 baselineDistance += GetContentOffsetY(layoutWrapper);
198 }
199 layoutWrapper->GetGeometryNode()->SetBaselineDistance(baselineDistance);
200 }
201
UpdateParagraph(LayoutWrapper * layoutWrapper)202 void TextLayoutAlgorithm::UpdateParagraph(LayoutWrapper* layoutWrapper)
203 {
204 int32_t spanTextLength = GetPreviousLength();
205 CHECK_NULL_VOID(layoutWrapper);
206 auto layoutProperty = layoutWrapper->GetLayoutProperty();
207 CHECK_NULL_VOID(layoutProperty);
208 auto frameNode = layoutWrapper->GetHostNode();
209 const auto& layoutConstrain = layoutProperty->CreateChildConstraint();
210 auto placeHolderLayoutConstrain = layoutConstrain;
211 placeHolderLayoutConstrain.maxSize.SetHeight(Infinity<float>());
212 placeHolderLayoutConstrain.percentReference.SetHeight(0);
213 const auto& children = layoutWrapper->GetAllChildrenWithBuild();
214 auto pattern = frameNode->GetPattern<TextPattern>();
215 CHECK_NULL_VOID(pattern);
216 auto aiSpanMap = pattern->GetAISpanMap();
217 for (const auto& child : spanItemChildren_) {
218 if (!child) {
219 continue;
220 }
221 auto imageSpanItem = AceType::DynamicCast<ImageSpanItem>(child);
222 if (imageSpanItem) {
223 int32_t targetId = imageSpanItem->imageNodeId;
224 auto iterItems = children.begin();
225 // find the Corresponding ImageNode for every ImageSpanItem
226 while (iterItems != children.end() && (*iterItems) && (*iterItems)->GetHostNode()->GetId() != targetId) {
227 iterItems++;
228 }
229 if (iterItems == children.end() || !(*iterItems)) {
230 continue;
231 }
232 (*iterItems)->Measure(layoutConstrain);
233 auto verticalAlign = VerticalAlign::BOTTOM;
234 auto imageLayoutProperty = DynamicCast<ImageLayoutProperty>((*iterItems)->GetLayoutProperty());
235 if (imageLayoutProperty) {
236 verticalAlign = imageLayoutProperty->GetVerticalAlign().value_or(VerticalAlign::BOTTOM);
237 }
238 auto geometryNode = (*iterItems)->GetGeometryNode();
239 if (!geometryNode) {
240 iterItems++;
241 continue;
242 }
243 auto width = geometryNode->GetMarginFrameSize().Width();
244 auto height = geometryNode->GetMarginFrameSize().Height();
245 child->placeholderIndex = child->UpdateParagraph(frameNode, paragraph_, width, height, verticalAlign);
246 child->content = " ";
247 child->position = spanTextLength + 1;
248 spanTextLength += 1;
249 iterItems++;
250 } else if (AceType::InstanceOf<PlaceholderSpanItem>(child)) {
251 auto placeholderSpanItem = AceType::DynamicCast<PlaceholderSpanItem>(child);
252 if (!placeholderSpanItem) {
253 continue;
254 }
255 int32_t targetId = placeholderSpanItem->placeholderSpanNodeId;
256 auto iterItems = children.begin();
257 // find the Corresponding ImageNode for every ImageSpanItem
258 while (iterItems != children.end() && (*iterItems) && (*iterItems)->GetHostNode()->GetId() != targetId) {
259 iterItems++;
260 }
261 if (iterItems == children.end() || !(*iterItems)) {
262 continue;
263 }
264 (*iterItems)->Measure(placeHolderLayoutConstrain);
265 auto geometryNode = (*iterItems)->GetGeometryNode();
266 if (!geometryNode) {
267 iterItems++;
268 continue;
269 }
270 auto width = geometryNode->GetMarginFrameSize().Width();
271 auto height = geometryNode->GetMarginFrameSize().Height();
272 child->placeholderIndex = child->UpdateParagraph(frameNode, paragraph_, width, height, VerticalAlign::NONE);
273 child->content = " ";
274 child->position = spanTextLength + 1;
275 spanTextLength += 1;
276 iterItems++;
277 } else if (child->unicode != 0) {
278 child->SetIsParentText(frameNode->GetTag() == V2::TEXT_ETS_TAG);
279 child->UpdateSymbolSpanParagraph(frameNode, paragraph_);
280 child->position = spanTextLength + SYMBOL_SPAN_LENGTH;
281 child->content = " ";
282 spanTextLength += SYMBOL_SPAN_LENGTH;
283 } else {
284 child->aiSpanMap = aiSpanMap;
285 child->UpdateParagraph(frameNode, paragraph_);
286 aiSpanMap = child->aiSpanMap;
287 child->position = spanTextLength + StringUtils::ToWstring(child->content).length();
288 spanTextLength += StringUtils::ToWstring(child->content).length();
289 }
290 }
291 }
292
UpdateParagraphForAISpan(const TextStyle & textStyle,LayoutWrapper * layoutWrapper)293 void TextLayoutAlgorithm::UpdateParagraphForAISpan(const TextStyle& textStyle, LayoutWrapper* layoutWrapper)
294 {
295 CHECK_NULL_VOID(layoutWrapper);
296 auto layoutProperty = layoutWrapper->GetLayoutProperty();
297 CHECK_NULL_VOID(layoutProperty);
298 auto frameNode = layoutWrapper->GetHostNode();
299 CHECK_NULL_VOID(frameNode);
300 auto pipeline = frameNode->GetContext();
301 CHECK_NULL_VOID(pipeline);
302 auto pattern = frameNode->GetPattern<TextPattern>();
303 CHECK_NULL_VOID(pattern);
304 auto textForAI = pattern->GetTextForAI();
305 auto wTextForAI = StringUtils::ToWstring(textForAI);
306 int32_t wTextForAILength = static_cast<int32_t>(wTextForAI.length());
307 int32_t preEnd = 0;
308 DragSpanPosition dragSpanPosition;
309 dragSpanPosition.dragStart = pattern->GetRecoverStart();
310 dragSpanPosition.dragEnd = pattern->GetRecoverEnd();
311 bool isDragging = pattern->IsDragging();
312 TextStyle aiSpanTextStyle = textStyle;
313 aiSpanTextStyle.SetTextColor(Color::BLUE);
314 aiSpanTextStyle.SetTextDecoration(TextDecoration::UNDERLINE);
315 aiSpanTextStyle.SetTextDecorationColor(Color::BLUE);
316 for (auto kv : pattern->GetAISpanMap()) {
317 if (preEnd >= wTextForAILength) {
318 break;
319 }
320 auto aiSpan = kv.second;
321 if (aiSpan.start < preEnd) {
322 TAG_LOGI(AceLogTag::ACE_TEXT, "Error prediction");
323 continue;
324 }
325 if (preEnd < aiSpan.start) {
326 dragSpanPosition.spanStart = preEnd;
327 dragSpanPosition.spanEnd = aiSpan.start;
328 GrayDisplayAISpan(dragSpanPosition, wTextForAI, textStyle, isDragging);
329 }
330 preEnd = aiSpan.end;
331 dragSpanPosition.spanStart = aiSpan.start;
332 dragSpanPosition.spanEnd = aiSpan.end;
333 GrayDisplayAISpan(dragSpanPosition, wTextForAI, aiSpanTextStyle, isDragging);
334 }
335 if (preEnd < wTextForAILength) {
336 dragSpanPosition.spanStart = preEnd;
337 dragSpanPosition.spanEnd = wTextForAILength;
338 GrayDisplayAISpan(dragSpanPosition, wTextForAI, textStyle, isDragging);
339 }
340 }
341
GrayDisplayAISpan(const DragSpanPosition & dragSpanPosition,const std::wstring wTextForAI,const TextStyle & textStyle,bool isDragging)342 void TextLayoutAlgorithm::GrayDisplayAISpan(const DragSpanPosition& dragSpanPosition,
343 const std::wstring wTextForAI, const TextStyle& textStyle, bool isDragging)
344 {
345 int32_t dragStart = dragSpanPosition.dragStart;
346 int32_t dragEnd = dragSpanPosition.dragEnd;
347 int32_t spanStart = dragSpanPosition.spanStart;
348 int32_t spanEnd = dragSpanPosition.spanEnd;
349 std::vector<std::string> contents = {};
350 std::string firstParagraph = "";
351 std::string secondParagraph = "";
352 std::string thirdParagraph = "";
353 if (dragStart > spanEnd || dragEnd < spanStart || !isDragging) {
354 firstParagraph = StringOutBoundProtection(spanStart, spanEnd - spanStart, wTextForAI);
355 } else if (spanStart <= dragStart && spanEnd >= dragStart && spanEnd <= dragEnd) {
356 firstParagraph = StringOutBoundProtection(spanStart, dragStart - spanStart, wTextForAI);
357 secondParagraph = StringOutBoundProtection(dragStart, spanEnd - dragStart, wTextForAI);
358 } else if (spanStart >= dragStart && spanEnd <= dragEnd) {
359 secondParagraph = StringOutBoundProtection(spanStart, spanEnd - spanStart, wTextForAI);
360 } else if (spanStart <= dragStart && spanEnd >= dragEnd) {
361 firstParagraph = StringOutBoundProtection(spanStart, dragStart - spanStart, wTextForAI);
362 secondParagraph = StringOutBoundProtection(dragStart, dragEnd - dragStart, wTextForAI);
363 thirdParagraph = StringOutBoundProtection(dragEnd, spanEnd - dragEnd, wTextForAI);
364 } else {
365 secondParagraph = StringOutBoundProtection(spanStart, dragEnd - spanStart, wTextForAI);
366 thirdParagraph = StringOutBoundProtection(dragEnd, spanEnd - dragEnd, wTextForAI);
367 }
368 contents = { firstParagraph, secondParagraph, thirdParagraph };
369 CreateParagraphDrag(textStyle, contents);
370 }
371
StringOutBoundProtection(int32_t position,int32_t length,std::wstring wTextForAI)372 std::string TextLayoutAlgorithm::StringOutBoundProtection(int32_t position, int32_t length, std::wstring wTextForAI)
373 {
374 int32_t wTextForAILength = static_cast<int32_t>(wTextForAI.length());
375 if (position >= wTextForAILength || length > wTextForAILength - position) {
376 return "";
377 } else {
378 return StringUtils::ToString(wTextForAI.substr(position, length));
379 }
380 }
381
CreateParagraph(const TextStyle & textStyle,std::string content,LayoutWrapper * layoutWrapper)382 bool TextLayoutAlgorithm::CreateParagraph(const TextStyle& textStyle, std::string content, LayoutWrapper* layoutWrapper)
383 {
384 auto frameNode = layoutWrapper->GetHostNode();
385 auto pipeline = frameNode->GetContext();
386 auto pattern = frameNode->GetPattern<TextPattern>();
387 auto paraStyle = GetParagraphStyle(textStyle, content, layoutWrapper);
388 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) && spanItemChildren_.empty()) {
389 paraStyle.fontSize = textStyle.GetFontSize().ConvertToPx();
390 }
391 paragraph_ = Paragraph::Create(paraStyle, FontCollection::Current());
392 CHECK_NULL_RETURN(paragraph_, false);
393 if (frameNode->GetTag() == V2::SYMBOL_ETS_TAG) {
394 auto layoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
395 CHECK_NULL_RETURN(layoutProperty, false);
396 auto symbolSourceInfo = layoutProperty->GetSymbolSourceInfo();
397 CHECK_NULL_RETURN(symbolSourceInfo, false);
398 TextStyle symbolTextStyle = textStyle;
399 symbolTextStyle.isSymbolGlyph_ = true;
400 symbolTextStyle.SetRenderStrategy(
401 symbolTextStyle.GetRenderStrategy() < 0 ? 0 : symbolTextStyle.GetRenderStrategy());
402 symbolTextStyle.SetEffectStrategy(
403 symbolTextStyle.GetEffectStrategy() < 0 ? 0 : symbolTextStyle.GetEffectStrategy());
404 paragraph_->PushStyle(symbolTextStyle);
405 paragraph_->AddSymbol(symbolSourceInfo->GetUnicode());
406 paragraph_->PopStyle();
407 paragraph_->Build();
408 paragraph_->SetParagraphSymbolAnimation(frameNode);
409 return true;
410 }
411 paragraph_->PushStyle(textStyle);
412 CHECK_NULL_RETURN(pattern, -1);
413 if (spanItemChildren_.empty()) {
414 if (pattern->NeedShowAIDetect()) {
415 UpdateParagraphForAISpan(textStyle, layoutWrapper);
416 } else {
417 if (pattern->IsDragging()) {
418 auto dragContents = pattern->GetDragContents();
419 CreateParagraphDrag(textStyle, dragContents);
420 } else {
421 StringUtils::TransformStrCase(content, static_cast<int32_t>(textStyle.GetTextCase()));
422 paragraph_->AddText(StringUtils::Str8ToStr16(content));
423 }
424 }
425 } else {
426 UpdateParagraph(layoutWrapper);
427 }
428 paragraph_->Build();
429 UpdateSymbolSpanEffect(frameNode);
430 return true;
431 }
432
UpdateSymbolSpanEffect(RefPtr<FrameNode> & frameNode)433 void TextLayoutAlgorithm::UpdateSymbolSpanEffect(RefPtr<FrameNode>& frameNode)
434 {
435 for (const auto& child : spanItemChildren_) {
436 if (!child || child->unicode == 0) {
437 continue;
438 }
439 if (child->GetTextStyle()->isSymbolGlyph_) {
440 paragraph_->SetParagraphSymbolAnimation(frameNode);
441 return;
442 }
443 }
444 }
445
CreateParagraphDrag(const TextStyle & textStyle,const std::vector<std::string> & contents)446 void TextLayoutAlgorithm::CreateParagraphDrag(const TextStyle& textStyle, const std::vector<std::string>& contents)
447 {
448 TextStyle dragTextStyle = textStyle;
449 Color color = textStyle.GetTextColor().ChangeAlpha(DRAGGED_TEXT_TRANSPARENCY);
450 dragTextStyle.SetTextColor(color);
451 std::vector<TextStyle> textStyles { textStyle, dragTextStyle, textStyle };
452
453 for (size_t i = 0; i < contents.size(); i++) {
454 std::string splitStr = contents[i];
455 if (splitStr.empty()) {
456 continue;
457 }
458 auto& style = textStyles[i];
459 paragraph_->PushStyle(style);
460 StringUtils::TransformStrCase(splitStr, static_cast<int32_t>(style.GetTextCase()));
461 paragraph_->AddText(StringUtils::Str8ToStr16(splitStr));
462 paragraph_->PopStyle();
463 }
464 }
465
CreateParagraphAndLayout(const TextStyle & textStyle,const std::string & content,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)466 bool TextLayoutAlgorithm::CreateParagraphAndLayout(const TextStyle& textStyle, const std::string& content,
467 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
468 {
469 if (!CreateParagraph(textStyle, content, layoutWrapper)) {
470 return false;
471 }
472 CHECK_NULL_RETURN(paragraph_, false);
473 auto maxSize = GetMaxMeasureSize(contentConstraint);
474 ApplyIndent(textStyle, maxSize.Width());
475 paragraph_->Layout(maxSize.Width());
476
477 bool ret = CreateImageSpanAndLayout(textStyle, content, contentConstraint, layoutWrapper);
478 return ret;
479 }
480
GetContentOffset(LayoutWrapper * layoutWrapper)481 OffsetF TextLayoutAlgorithm::GetContentOffset(LayoutWrapper* layoutWrapper)
482 {
483 OffsetF contentOffset(0.0, 0.0);
484 CHECK_NULL_RETURN(layoutWrapper, contentOffset);
485
486 auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
487 const auto& padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
488 MinusPaddingToSize(padding, size);
489 auto left = padding.left.value_or(0);
490 auto top = padding.top.value_or(0);
491 auto paddingOffset = OffsetF(left, top);
492 auto align = Alignment::CENTER;
493 if (layoutWrapper->GetLayoutProperty()->GetPositionProperty()) {
494 align = layoutWrapper->GetLayoutProperty()->GetPositionProperty()->GetAlignment().value_or(align);
495 }
496
497 const auto& content = layoutWrapper->GetGeometryNode()->GetContent();
498 if (content) {
499 contentOffset = Alignment::GetAlignPosition(size, content->GetRect().GetSize(), align) + paddingOffset;
500 content->SetOffset(contentOffset);
501 }
502 return contentOffset;
503 }
504
Layout(LayoutWrapper * layoutWrapper)505 void TextLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
506 {
507 CHECK_NULL_VOID(layoutWrapper);
508 auto contentOffset = GetContentOffset(layoutWrapper);
509 std::vector<int32_t> placeholderIndex;
510 for (const auto& child : spanItemChildren_) {
511 if (!child) {
512 continue;
513 }
514 if (AceType::InstanceOf<ImageSpanItem>(child) || AceType::InstanceOf<PlaceholderSpanItem>(child)) {
515 placeholderIndex.emplace_back(child->placeholderIndex);
516 }
517 }
518 if (spanItemChildren_.empty() || placeholderIndex.empty()) {
519 return;
520 }
521
522 size_t index = 0;
523 std::vector<RectF> rectsForPlaceholders;
524 GetPlaceholderRects(rectsForPlaceholders);
525 const auto& children = layoutWrapper->GetAllChildrenWithBuild();
526 // children only contains the image span.
527 for (const auto& child : children) {
528 if (!child) {
529 ++index;
530 continue;
531 }
532 if (index >= placeholderIndex.size() ||
533 (index >= rectsForPlaceholders.size() && child->GetHostTag() != V2::PLACEHOLDER_SPAN_ETS_TAG)) {
534 child->SetActive(false);
535 continue;
536 }
537 child->SetActive(true);
538 auto rect = rectsForPlaceholders.at(index) - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
539 auto geometryNode = child->GetGeometryNode();
540 if (!geometryNode) {
541 ++index;
542 continue;
543 }
544 geometryNode->SetMarginFrameOffset(contentOffset + OffsetF(rect.Left(), rect.Top()));
545 child->Layout();
546 ++index;
547 }
548
549 auto frameNode = layoutWrapper->GetHostNode();
550 CHECK_NULL_VOID(frameNode);
551 auto pipeline = frameNode->GetContext();
552 CHECK_NULL_VOID(pipeline);
553 auto pattern = frameNode->GetPattern<TextPattern>();
554 CHECK_NULL_VOID(pattern);
555 pattern->InitSpanImageLayout(placeholderIndex, rectsForPlaceholders, contentOffset);
556 }
557
AdaptMinTextSize(TextStyle & textStyle,const std::string & content,const LayoutConstraintF & contentConstraint,const RefPtr<PipelineContext> & pipeline,LayoutWrapper * layoutWrapper)558 bool TextLayoutAlgorithm::AdaptMinTextSize(TextStyle& textStyle, const std::string& content,
559 const LayoutConstraintF& contentConstraint, const RefPtr<PipelineContext>& pipeline, LayoutWrapper* layoutWrapper)
560 {
561 double maxFontSize = 0.0;
562 double minFontSize = 0.0;
563 if (!textStyle.GetAdaptMaxFontSize().NormalizeToPx(pipeline->GetDipScale(), pipeline->GetFontScale(),
564 pipeline->GetLogicScale(), contentConstraint.maxSize.Height(), maxFontSize)) {
565 return false;
566 }
567 if (!textStyle.GetAdaptMinFontSize().NormalizeToPx(pipeline->GetDipScale(), pipeline->GetFontScale(),
568 pipeline->GetLogicScale(), contentConstraint.maxSize.Height(), minFontSize)) {
569 return false;
570 }
571 if (LessNotEqual(maxFontSize, minFontSize) || LessOrEqual(minFontSize, 0.0)) {
572 if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) {
573 TAG_LOGE(AceLogTag::ACE_TEXT, "create paragraph error");
574 return false;
575 }
576 return true;
577 }
578 constexpr Dimension ADAPT_UNIT = 1.0_fp;
579 Dimension step = ADAPT_UNIT;
580 if (GreatNotEqual(textStyle.GetAdaptFontSizeStep().Value(), 0.0)) {
581 step = textStyle.GetAdaptFontSizeStep();
582 }
583 double stepSize = 0.0;
584 if (!step.NormalizeToPx(pipeline->GetDipScale(), pipeline->GetFontScale(), pipeline->GetLogicScale(),
585 contentConstraint.maxSize.Height(), stepSize)) {
586 return false;
587 }
588 auto maxSize = GetMaxMeasureSize(contentConstraint);
589 while (GreatOrEqual(maxFontSize, minFontSize)) {
590 textStyle.SetFontSize(Dimension(maxFontSize));
591 if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) {
592 TAG_LOGE(AceLogTag::ACE_TEXT, "create paragraph error");
593 return false;
594 }
595 if (!DidExceedMaxLines(maxSize)) {
596 break;
597 }
598 maxFontSize -= stepSize;
599 }
600 return true;
601 }
602
DidExceedMaxLines(const SizeF & maxSize)603 bool TextLayoutAlgorithm::DidExceedMaxLines(const SizeF& maxSize)
604 {
605 CHECK_NULL_RETURN(paragraph_, false);
606 bool didExceedMaxLines = paragraph_->DidExceedMaxLines();
607 didExceedMaxLines = didExceedMaxLines || GreatNotEqual(paragraph_->GetHeight(), maxSize.Height());
608 didExceedMaxLines = didExceedMaxLines || GreatNotEqual(paragraph_->GetLongestLine(), maxSize.Width());
609 return didExceedMaxLines;
610 }
611
GetTextDirection(const std::string & content,LayoutWrapper * layoutWrapper)612 TextDirection TextLayoutAlgorithm::GetTextDirection(const std::string& content, LayoutWrapper* layoutWrapper)
613 {
614 auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
615 CHECK_NULL_RETURN(textLayoutProperty, TextDirection::LTR);
616 auto direction = textLayoutProperty->GetLayoutDirection();
617 if (direction == TextDirection::LTR || direction == TextDirection::RTL) {
618 return direction;
619 }
620
621 TextDirection textDirection = TextDirection::LTR;
622 auto showingTextForWString = StringUtils::ToWstring(content);
623 for (const auto& charOfShowingText : showingTextForWString) {
624 if (TextLayoutadapter::IsLeftToRight(charOfShowingText)) {
625 return TextDirection::LTR;
626 } else if (TextLayoutadapter::IsRightToLeft(charOfShowingText)) {
627 return TextDirection::RTL;
628 } else if (TextLayoutadapter::IsRightTOLeftArabic(charOfShowingText)) {
629 return TextDirection::RTL;
630 }
631 }
632 return textDirection;
633 }
634
GetTextWidth() const635 float TextLayoutAlgorithm::GetTextWidth() const
636 {
637 CHECK_NULL_RETURN(paragraph_, 0.0);
638 return paragraph_->GetTextWidth();
639 }
640
GetParagraph()641 const RefPtr<Paragraph>& TextLayoutAlgorithm::GetParagraph()
642 {
643 return paragraph_;
644 }
645
GetBaselineOffset() const646 float TextLayoutAlgorithm::GetBaselineOffset() const
647 {
648 return baselineOffset_;
649 }
650
GetMaxMeasureSize(const LayoutConstraintF & contentConstraint) const651 SizeF TextLayoutAlgorithm::GetMaxMeasureSize(const LayoutConstraintF& contentConstraint) const
652 {
653 auto maxSize = contentConstraint.selfIdealSize;
654 maxSize.UpdateIllegalSizeWithCheck(contentConstraint.maxSize);
655 return maxSize.ConvertToSizeT();
656 }
657
GetSpanItemChildren()658 std::list<RefPtr<SpanItem>>&& TextLayoutAlgorithm::GetSpanItemChildren()
659 {
660 return std::move(spanItemChildren_);
661 }
662
BuildParagraph(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,const RefPtr<PipelineContext> & pipeline,LayoutWrapper * layoutWrapper)663 bool TextLayoutAlgorithm::BuildParagraph(TextStyle& textStyle, const RefPtr<TextLayoutProperty>& layoutProperty,
664 const LayoutConstraintF& contentConstraint, const RefPtr<PipelineContext>& pipeline, LayoutWrapper* layoutWrapper)
665 {
666 if (!textStyle.GetAdaptTextSize()) {
667 if (!CreateParagraphAndLayout(
668 textStyle, layoutProperty->GetContent().value_or(""), contentConstraint, layoutWrapper)) {
669 TAG_LOGE(AceLogTag::ACE_TEXT, "create paragraph error");
670 return false;
671 }
672 } else {
673 if (!AdaptMinTextSize(
674 textStyle, layoutProperty->GetContent().value_or(""), contentConstraint, pipeline, layoutWrapper)) {
675 return false;
676 }
677 }
678
679 // Confirmed specification: The width of the text paragraph covers the width of the component, so this code is
680 // generally not allowed to be modified
681 if (!contentConstraint.selfIdealSize.Width()) {
682 float paragraphNewWidth = std::min(std::min(GetTextWidth(), paragraph_->GetMaxWidth()) + indent_,
683 GetMaxMeasureSize(contentConstraint).Width());
684 paragraphNewWidth =
685 std::clamp(paragraphNewWidth, contentConstraint.minSize.Width(), contentConstraint.maxSize.Width());
686 if (!NearEqual(paragraphNewWidth, paragraph_->GetMaxWidth())) {
687 paragraph_->Layout(std::ceil(paragraphNewWidth));
688 }
689 }
690 return true;
691 }
692
BuildParagraphAdaptUseMinFontSize(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,const RefPtr<PipelineContext> & pipeline,LayoutWrapper * layoutWrapper)693 bool TextLayoutAlgorithm::BuildParagraphAdaptUseMinFontSize(TextStyle& textStyle,
694 const RefPtr<TextLayoutProperty>& layoutProperty, const LayoutConstraintF& contentConstraint,
695 const RefPtr<PipelineContext>& pipeline, LayoutWrapper* layoutWrapper)
696 {
697 if (!AdaptMaxTextSize(
698 textStyle, layoutProperty->GetContent().value_or(""), contentConstraint, pipeline, layoutWrapper)) {
699 return false;
700 }
701
702 // Confirmed specification: The width of the text paragraph covers the width of the component, so this code is
703 // generally not allowed to be modified
704 if (!contentConstraint.selfIdealSize.Width()) {
705 float paragraphNewWidth = std::min(std::min(GetTextWidth(), paragraph_->GetMaxWidth()) + indent_,
706 GetMaxMeasureSize(contentConstraint).Width());
707 paragraphNewWidth =
708 std::clamp(paragraphNewWidth, contentConstraint.minSize.Width(), contentConstraint.maxSize.Width());
709 if (!NearEqual(paragraphNewWidth, paragraph_->GetMaxWidth())) {
710 paragraph_->Layout(std::ceil(paragraphNewWidth));
711 }
712 }
713
714 return true;
715 }
716
BuildParagraphAdaptUseLayoutConstraint(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,const RefPtr<PipelineContext> & pipeline,LayoutWrapper * layoutWrapper)717 bool TextLayoutAlgorithm::BuildParagraphAdaptUseLayoutConstraint(TextStyle& textStyle,
718 const RefPtr<TextLayoutProperty>& layoutProperty, const LayoutConstraintF& contentConstraint,
719 const RefPtr<PipelineContext>& pipeline, LayoutWrapper* layoutWrapper)
720 {
721 // Create the paragraph and obtain the height.
722 if (!BuildParagraph(textStyle, layoutProperty, contentConstraint, pipeline, layoutWrapper)) {
723 return false;
724 }
725 auto height = static_cast<float>(paragraph_->GetHeight());
726 double minTextSizeHeight = 0.0;
727 textStyle.GetAdaptMinFontSize().NormalizeToPx(
728 pipeline->GetDipScale(), pipeline->GetFontScale(), pipeline->GetLogicScale(), height, minTextSizeHeight);
729 if (LessOrEqual(minTextSizeHeight, 0.0)) {
730 textStyle.GetFontSize().NormalizeToPx(
731 pipeline->GetDipScale(), pipeline->GetFontScale(), pipeline->GetLogicScale(), height, minTextSizeHeight);
732 }
733 if (textStyle.GetMaxLines() == UINT32_MAX) {
734 double baselineOffset = 0.0;
735 textStyle.GetBaselineOffset().NormalizeToPx(pipeline->GetDipScale(), pipeline->GetFontScale(),
736 pipeline->GetLogicScale(), contentConstraint.maxSize.Height(), baselineOffset);
737 double lineHeight = minTextSizeHeight;
738 if (textStyle.HasHeightOverride()) {
739 textStyle.GetLineHeight().NormalizeToPx(pipeline->GetDipScale(), pipeline->GetFontScale(),
740 pipeline->GetLogicScale(), minTextSizeHeight, lineHeight);
741 }
742 uint32_t maxLines = (contentConstraint.maxSize.Height() - baselineOffset - minTextSizeHeight) / (lineHeight);
743 textStyle.SetMaxLines(maxLines);
744 textStyle.DisableAdaptTextSize();
745
746 if (!BuildParagraph(textStyle, layoutProperty, contentConstraint, pipeline, layoutWrapper)) {
747 return false;
748 }
749 }
750 // Reducing the value of MaxLines to make sure the parapraph could be layout in the constraint of height.
751 height = static_cast<float>(paragraph_->GetHeight());
752 while (GreatNotEqual(height, contentConstraint.maxSize.Height())) {
753 auto maxLines = textStyle.GetMaxLines();
754 if (maxLines == 0) {
755 break;
756 } else {
757 maxLines = textStyle.GetMaxLines() - 1;
758 textStyle.SetMaxLines(maxLines);
759 }
760 if (!BuildParagraph(textStyle, layoutProperty, contentConstraint, pipeline, layoutWrapper)) {
761 return false;
762 }
763 height = static_cast<float>(paragraph_->GetHeight());
764 }
765 return true;
766 }
767
BuildTextRaceParagraph(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,const RefPtr<PipelineContext> & pipeline,LayoutWrapper * layoutWrapper)768 std::optional<SizeF> TextLayoutAlgorithm::BuildTextRaceParagraph(TextStyle& textStyle,
769 const RefPtr<TextLayoutProperty>& layoutProperty, const LayoutConstraintF& contentConstraint,
770 const RefPtr<PipelineContext>& pipeline, LayoutWrapper* layoutWrapper)
771 {
772 // create a paragraph with all text in 1 line
773 textStyle.SetTextOverflow(TextOverflow::CLIP);
774 textStyle.SetMaxLines(1);
775 if (!CreateParagraph(textStyle, layoutProperty->GetContent().value_or(""), layoutWrapper)) {
776 return std::nullopt;
777 }
778 if (!paragraph_) {
779 return std::nullopt;
780 }
781
782 // layout the paragraph to the width of text
783 paragraph_->Layout(std::numeric_limits<float>::max());
784 float paragraphWidth = GetTextWidth();
785 if (contentConstraint.selfIdealSize.Width().has_value()) {
786 paragraphWidth = std::max(contentConstraint.selfIdealSize.Width().value(), paragraphWidth);
787 } else {
788 paragraphWidth = std::max(contentConstraint.maxSize.Width(), paragraphWidth);
789 }
790 paragraph_->Layout(std::ceil(paragraphWidth));
791
792 textStyle_ = textStyle;
793
794 // calculate the content size
795 auto height = static_cast<float>(paragraph_->GetHeight());
796 double baselineOffset = 0.0;
797 if (layoutProperty->GetBaselineOffsetValue(Dimension())
798 .NormalizeToPx(
799 pipeline->GetDipScale(), pipeline->GetFontScale(), pipeline->GetLogicScale(), height, baselineOffset)) {
800 baselineOffset_ = static_cast<float>(baselineOffset);
801 }
802 float heightFinal =
803 std::min(static_cast<float>(height + std::fabs(baselineOffset)), contentConstraint.maxSize.Height());
804
805 float widthFinal = paragraphWidth;
806 if (contentConstraint.selfIdealSize.Width().has_value()) {
807 if (contentConstraint.selfIdealSize.Width().value() < paragraphWidth) {
808 widthFinal = contentConstraint.selfIdealSize.Width().value();
809 }
810 } else if (contentConstraint.maxSize.Width() < paragraphWidth) {
811 widthFinal = contentConstraint.maxSize.Width();
812 }
813 return SizeF(widthFinal, heightFinal);
814 }
815
SetPropertyToModifier(const RefPtr<TextLayoutProperty> & layoutProperty,RefPtr<TextContentModifier> modifier)816 void TextLayoutAlgorithm::SetPropertyToModifier(
817 const RefPtr<TextLayoutProperty>& layoutProperty, RefPtr<TextContentModifier> modifier)
818 {
819 auto fontFamily = layoutProperty->GetFontFamily();
820 if (fontFamily.has_value()) {
821 modifier->SetFontFamilies(fontFamily.value());
822 }
823 auto fontSize = layoutProperty->GetFontSize();
824 if (fontSize.has_value()) {
825 modifier->SetFontSize(fontSize.value());
826 }
827 auto fontWeight = layoutProperty->GetFontWeight();
828 if (fontWeight.has_value()) {
829 modifier->SetFontWeight(fontWeight.value());
830 }
831 auto textColor = layoutProperty->GetTextColor();
832 if (textColor.has_value()) {
833 modifier->SetTextColor(textColor.value());
834 }
835 auto textShadow = layoutProperty->GetTextShadow();
836 if (textShadow.has_value()) {
837 modifier->SetTextShadow(textShadow.value());
838 }
839 auto textDecorationColor = layoutProperty->GetTextDecorationColor();
840 if (textDecorationColor.has_value()) {
841 modifier->SetTextDecorationColor(textDecorationColor.value());
842 }
843 auto textDecorationStyle = layoutProperty->GetTextDecorationStyle();
844 if (textDecorationStyle.has_value()) {
845 modifier->SetTextDecorationStyle(textDecorationStyle.value());
846 }
847 auto textDecoration = layoutProperty->GetTextDecoration();
848 if (textDecoration.has_value()) {
849 modifier->SetTextDecoration(textDecoration.value());
850 }
851 auto baselineOffset = layoutProperty->GetBaselineOffset();
852 if (baselineOffset.has_value()) {
853 modifier->SetBaselineOffset(baselineOffset.value());
854 }
855 }
856
AdaptMaxTextSize(TextStyle & textStyle,const std::string & content,const LayoutConstraintF & contentConstraint,const RefPtr<PipelineContext> & pipeline,LayoutWrapper * layoutWrapper)857 bool TextLayoutAlgorithm::AdaptMaxTextSize(TextStyle& textStyle, const std::string& content,
858 const LayoutConstraintF& contentConstraint, const RefPtr<PipelineContext>& pipeline, LayoutWrapper* layoutWrapper)
859 {
860 double maxFontSize = 0.0;
861 double minFontSize = 0.0;
862 if (!textStyle.GetAdaptMaxFontSize().NormalizeToPx(pipeline->GetDipScale(), pipeline->GetFontScale(),
863 pipeline->GetLogicScale(), contentConstraint.maxSize.Height(), maxFontSize)) {
864 return false;
865 }
866 if (!textStyle.GetAdaptMinFontSize().NormalizeToPx(pipeline->GetDipScale(), pipeline->GetFontScale(),
867 pipeline->GetLogicScale(), contentConstraint.maxSize.Height(), minFontSize)) {
868 return false;
869 }
870 if (LessNotEqual(maxFontSize, minFontSize) || LessOrEqual(minFontSize, 0.0)) {
871 // minFontSize or maxFontSize is invalid
872 if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) {
873 TAG_LOGE(AceLogTag::ACE_TEXT, "create paragraph error");
874 return false;
875 }
876 return true;
877 }
878 constexpr Dimension ADAPT_UNIT = 1.0_fp;
879
880 auto textLayoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
881 CHECK_NULL_RETURN(textLayoutProperty, false);
882 auto step = textLayoutProperty->GetAdaptFontSizeStepValue(ADAPT_UNIT);
883 if (GreatNotEqual(textStyle.GetAdaptFontSizeStep().Value(), 0.0)) {
884 step = textStyle.GetAdaptFontSizeStep();
885 }
886 double stepSize = 0.0;
887 if (!step.NormalizeToPx(pipeline->GetDipScale(), pipeline->GetFontScale(), pipeline->GetLogicScale(),
888 contentConstraint.maxSize.Height(), stepSize)) {
889 return false;
890 }
891 auto maxSize = GetMaxMeasureSize(contentConstraint);
892 // Use the minFontSize to layout the paragraph. While using the minFontSize, if the paragraph could be layout in 1
893 // line, then increase the font size and try to layout using the maximum available fontsize.
894 textStyle.SetFontSize(Dimension(minFontSize));
895 if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) {
896 TAG_LOGE(AceLogTag::ACE_TEXT, "create paragraph error");
897 return false;
898 }
899 if (paragraph_->GetLineCount() > 1 || paragraph_->DidExceedMaxLines() ||
900 GreatNotEqual(paragraph_->GetLongestLine(), maxSize.Width())) {
901 return true;
902 }
903 auto tag = static_cast<int32_t>((maxFontSize - minFontSize) / stepSize);
904 auto length = tag + 1 + (GreatNotEqual(maxFontSize, minFontSize + stepSize * tag) ? 1 : 0);
905 int32_t left = 0;
906 int32_t right = length - 1;
907 float fontSize = 0.0f;
908 while (left <= right) {
909 int32_t mid = left + (right - left) / 2;
910 if (mid == length - 1) {
911 fontSize = static_cast<float>(maxFontSize);
912 } else {
913 fontSize = static_cast<float>(minFontSize + stepSize * mid);
914 }
915 textStyle.SetFontSize(Dimension(fontSize));
916 if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) {
917 TAG_LOGE(AceLogTag::ACE_TEXT, "create paragraph error");
918 return false;
919 }
920 if (paragraph_->GetLineCount() <= 1 && !paragraph_->DidExceedMaxLines() &&
921 LessNotEqual(paragraph_->GetLongestLine(), maxSize.Width())) {
922 left = mid + 1;
923 } else {
924 right = mid - 1;
925 }
926 }
927 if (left - 1 == length - 1) {
928 fontSize = static_cast<float>(maxFontSize);
929 } else {
930 fontSize = static_cast<float>(minFontSize + stepSize * (left - 1));
931 }
932 textStyle.SetFontSize(Dimension(fontSize));
933
934 TAG_LOGD(AceLogTag::ACE_TEXT,
935 "MIN_FONT_SIZE_FIRST, adapt maxTextSize, length: %{public}d result: %{public}d fontSize: %{public}f ", length,
936 left - 1, fontSize);
937
938 if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) {
939 TAG_LOGE(AceLogTag::ACE_TEXT, "create paragraph error");
940 return false;
941 }
942 return true;
943 }
944
GetTextStyle() const945 std::optional<TextStyle> TextLayoutAlgorithm::GetTextStyle() const
946 {
947 return textStyle_;
948 }
949
UpdateTextColorIfForeground(const RefPtr<FrameNode> & frameNode,TextStyle & textStyle)950 void TextLayoutAlgorithm::UpdateTextColorIfForeground(const RefPtr<FrameNode>& frameNode, TextStyle& textStyle)
951 {
952 auto renderContext = frameNode->GetRenderContext();
953 if (renderContext->HasForegroundColor()) {
954 if (renderContext->GetForegroundColorValue().GetValue() != textStyle.GetTextColor().GetValue()) {
955 textStyle.SetTextColor(Color::FOREGROUND);
956 }
957 } else if (renderContext->HasForegroundColorStrategy()) {
958 textStyle.SetTextColor(Color::FOREGROUND);
959 }
960 }
961
ApplyIndent(const TextStyle & textStyle,double width)962 void TextLayoutAlgorithm::ApplyIndent(const TextStyle& textStyle, double width)
963 {
964 if (LessOrEqual(textStyle.GetTextIndent().Value(), 0.0)) {
965 return;
966 }
967 // first line indent
968 CHECK_NULL_VOID(paragraph_);
969 auto pipeline = PipelineContext::GetCurrentContext();
970 CHECK_NULL_VOID(pipeline);
971 double indent = 0.0;
972 if (textStyle.GetTextIndent().Unit() != DimensionUnit::PERCENT) {
973 if (!textStyle.GetTextIndent().NormalizeToPx(
974 pipeline->GetDipScale(), pipeline->GetFontScale(), pipeline->GetLogicScale(), width, indent)) {
975 return;
976 }
977 } else {
978 indent = width * textStyle.GetTextIndent().Value();
979 }
980 indent_ = static_cast<float>(indent);
981 std::vector<float> indents;
982 // only indent first line
983 indents.emplace_back(indent_);
984 indents.emplace_back(0.0);
985 paragraph_->SetIndents(indents);
986 }
987
IncludeImageSpan(LayoutWrapper * layoutWrapper)988 bool TextLayoutAlgorithm::IncludeImageSpan(LayoutWrapper* layoutWrapper)
989 {
990 CHECK_NULL_RETURN(layoutWrapper, false);
991 return (!layoutWrapper->GetAllChildrenWithBuild().empty());
992 }
993
GetSpanAndImageSpanList(std::list<RefPtr<SpanItem>> & spanList,std::map<int32_t,std::pair<RectF,RefPtr<PlaceholderSpanItem>>> & placeholderSpanList)994 void TextLayoutAlgorithm::GetSpanAndImageSpanList(std::list<RefPtr<SpanItem>>& spanList,
995 std::map<int32_t, std::pair<RectF, RefPtr<PlaceholderSpanItem>>>& placeholderSpanList)
996 {
997 std::vector<RectF> rectsForPlaceholders;
998 paragraph_->GetRectsForPlaceholders(rectsForPlaceholders);
999 for (const auto& child : spanItemChildren_) {
1000 if (!child) {
1001 continue;
1002 }
1003 auto imageSpanItem = AceType::DynamicCast<ImageSpanItem>(child);
1004 if (imageSpanItem) {
1005 int32_t index = child->placeholderIndex;
1006 if (index >= 0 && index < static_cast<int32_t>(rectsForPlaceholders.size())) {
1007 placeholderSpanList.emplace(index, std::make_pair(rectsForPlaceholders.at(index), imageSpanItem));
1008 }
1009 } else if (auto placeholderSpanItem = AceType::DynamicCast<PlaceholderSpanItem>(child); placeholderSpanItem) {
1010 int32_t index = child->placeholderIndex;
1011 if (index >= 0 && index < static_cast<int32_t>(rectsForPlaceholders.size())) {
1012 placeholderSpanList.emplace(index, std::make_pair(rectsForPlaceholders.at(index), placeholderSpanItem));
1013 }
1014 } else {
1015 spanList.emplace_back(child);
1016 }
1017 }
1018 }
1019
SplitSpanContentByLines(const TextStyle & textStyle,const std::list<RefPtr<SpanItem>> & spanList,std::map<int32_t,std::pair<RectF,std::list<RefPtr<SpanItem>>>> & spanContentLines)1020 void TextLayoutAlgorithm::SplitSpanContentByLines(const TextStyle& textStyle,
1021 const std::list<RefPtr<SpanItem>>& spanList,
1022 std::map<int32_t, std::pair<RectF, std::list<RefPtr<SpanItem>>>>& spanContentLines)
1023 {
1024 int32_t currentLine = 0;
1025 int32_t start = GetFirstSpanStartPositon();
1026 for (const auto& child : spanList) {
1027 if (!child) {
1028 continue;
1029 }
1030 std::string textValue = child->content;
1031 std::vector<RectF> selectedRects;
1032 if (!textValue.empty()) {
1033 paragraph_->GetRectsForRange(child->position - StringUtils::ToWstring(textValue).length() - start,
1034 child->position - start, selectedRects);
1035 }
1036 RectF currentRect = RectF(0, -1, 0, 0);
1037 auto preLinetLastSpan = spanContentLines.rbegin();
1038 double preLineFontSize = textStyle.GetFontSize().Value();
1039 if (preLinetLastSpan != spanContentLines.rend()) {
1040 currentRect = preLinetLastSpan->second.first;
1041 if (preLinetLastSpan->second.second.back() && preLinetLastSpan->second.second.back()->fontStyle &&
1042 preLinetLastSpan->second.second.back()->fontStyle->GetFontSize().has_value()) {
1043 preLineFontSize = preLinetLastSpan->second.second.back()->fontStyle->GetFontSize().value().Value();
1044 }
1045 }
1046 for (const auto& rect : selectedRects) {
1047 if (!NearEqual(currentRect.GetOffset().GetY(), rect.GetOffset().GetY())) {
1048 currentRect = rect;
1049 ++currentLine;
1050 }
1051
1052 auto iter = spanContentLines.find(currentLine);
1053 if (iter != spanContentLines.end()) {
1054 auto iterSecond = std::find(iter->second.second.begin(), iter->second.second.end(), child);
1055 if (iterSecond != iter->second.second.end()) {
1056 continue;
1057 }
1058 if (NearEqual(rect.GetOffset().GetY(), currentRect.GetOffset().GetY()) && child->fontStyle &&
1059 child->fontStyle->GetFontSize().has_value() &&
1060 NearEqual(preLineFontSize, child->fontStyle->GetFontSize().value().Value())) {
1061 continue;
1062 }
1063 iter->second.second.emplace_back(child);
1064 } else {
1065 std::list<RefPtr<SpanItem>> spanLineList;
1066 spanLineList.emplace_back(child);
1067 spanContentLines.emplace(currentLine, std::make_pair(currentRect, spanLineList));
1068 }
1069 }
1070 }
1071 }
1072
GetFirstSpanStartPositon()1073 int32_t TextLayoutAlgorithm::GetFirstSpanStartPositon()
1074 {
1075 int32_t start = 0;
1076 if (!spanItemChildren_.empty()) {
1077 auto firstSpan = spanItemChildren_.front();
1078 if (firstSpan) {
1079 start = firstSpan->position - static_cast<int32_t>(StringUtils::ToWstring(firstSpan->content).length());
1080 }
1081 }
1082 return start;
1083 }
1084
SetImageSpanTextStyleByLines(const TextStyle & textStyle,std::map<int32_t,std::pair<RectF,RefPtr<PlaceholderSpanItem>>> & placeholderSpanList,std::map<int32_t,std::pair<RectF,std::list<RefPtr<SpanItem>>>> & spanContentLines)1085 void TextLayoutAlgorithm::SetImageSpanTextStyleByLines(const TextStyle& textStyle,
1086 std::map<int32_t, std::pair<RectF, RefPtr<PlaceholderSpanItem>>>& placeholderSpanList,
1087 std::map<int32_t, std::pair<RectF, std::list<RefPtr<SpanItem>>>>& spanContentLines)
1088 {
1089 auto pipelineContext = PipelineContext::GetCurrentContext();
1090 CHECK_NULL_VOID(pipelineContext);
1091
1092 auto placeholderItem = placeholderSpanList.begin();
1093 for (auto spanItem = spanContentLines.begin(); spanItem != spanContentLines.end(); spanItem++) {
1094 for (; placeholderItem != placeholderSpanList.end();) {
1095 auto placeholder = placeholderItem->second.second;
1096 if (!placeholder) {
1097 continue;
1098 }
1099 auto offset = placeholderItem->second.first.GetOffset();
1100 auto spanItemRect = spanItem->second.first;
1101 if (GreatOrEqual(offset.GetY(), spanItemRect.Bottom())) {
1102 break;
1103 }
1104 auto placeholderItemRect = placeholderItem->second.first;
1105 placeholderItemRect.SetOffset(OffsetF(spanItemRect.GetOffset().GetX(), offset.GetY()));
1106 bool isIntersectWith = spanItem->second.first.IsIntersectWith(placeholderItemRect);
1107 if (!isIntersectWith) {
1108 placeholderItem++;
1109 continue;
1110 }
1111 Dimension maxFontSize;
1112 TextStyle spanTextStyle = textStyle;
1113 for (const auto& child : spanItem->second.second) {
1114 if (!child || !child->fontStyle) {
1115 continue;
1116 }
1117 if (!child->fontStyle->GetFontSize().has_value()) {
1118 continue;
1119 }
1120 if (LessNotEqual(maxFontSize.Value(), child->fontStyle->GetFontSize().value().Value())) {
1121 maxFontSize = child->fontStyle->GetFontSize().value();
1122 spanTextStyle = CreateTextStyleUsingTheme(
1123 child->fontStyle, child->textLineStyle, pipelineContext->GetTheme<TextTheme>());
1124 }
1125 }
1126 placeholder->textStyle = spanTextStyle;
1127 placeholderItem++;
1128 }
1129 }
1130 }
1131
SetImageSpanTextStyle(const TextStyle & textStyle)1132 void TextLayoutAlgorithm::SetImageSpanTextStyle(const TextStyle& textStyle)
1133 {
1134 CHECK_NULL_VOID(paragraph_);
1135
1136 std::list<RefPtr<SpanItem>> spanList;
1137 std::map<int32_t, std::pair<RectF, RefPtr<PlaceholderSpanItem>>> placeholderList;
1138 GetSpanAndImageSpanList(spanList, placeholderList);
1139
1140 // split text content by lines
1141 std::map<int32_t, std::pair<RectF, std::list<RefPtr<SpanItem>>>> spanContentLines;
1142 SplitSpanContentByLines(textStyle, spanList, spanContentLines);
1143
1144 // set imagespan textstyle
1145 SetImageSpanTextStyleByLines(textStyle, placeholderList, spanContentLines);
1146 }
1147
CreateImageSpanAndLayout(const TextStyle & textStyle,const std::string & content,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)1148 bool TextLayoutAlgorithm::CreateImageSpanAndLayout(const TextStyle& textStyle, const std::string& content,
1149 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
1150 {
1151 bool includeImageSpan = IncludeImageSpan(layoutWrapper);
1152 if (includeImageSpan) {
1153 SetImageSpanTextStyle(textStyle);
1154 if (!CreateParagraph(textStyle, content, layoutWrapper)) {
1155 return false;
1156 }
1157 CHECK_NULL_RETURN(paragraph_, false);
1158 auto maxSize = GetMaxMeasureSize(contentConstraint);
1159 ApplyIndent(textStyle, maxSize.Width());
1160 paragraph_->Layout(maxSize.Width());
1161 }
1162 return true;
1163 }
1164
GetLineCount() const1165 size_t TextLayoutAlgorithm::GetLineCount() const
1166 {
1167 CHECK_NULL_RETURN(paragraph_, 0);
1168 return paragraph_->GetLineCount();
1169 }
1170
GetPlaceholderRects(std::vector<RectF> & rects)1171 void TextLayoutAlgorithm::GetPlaceholderRects(std::vector<RectF>& rects)
1172 {
1173 CHECK_NULL_VOID(paragraph_);
1174 paragraph_->GetRectsForPlaceholders(rects);
1175 }
1176
GetParagraphStyle(const TextStyle & textStyle,const std::string & content,LayoutWrapper * layoutWrapper) const1177 ParagraphStyle TextLayoutAlgorithm::GetParagraphStyle(
1178 const TextStyle& textStyle, const std::string& content, LayoutWrapper* layoutWrapper) const
1179 {
1180 return {
1181 .direction = GetTextDirection(content, layoutWrapper),
1182 .align = textStyle.GetTextAlign(),
1183 .maxLines = textStyle.GetMaxLines(),
1184 .fontLocale = Localization::GetInstance()->GetFontLocale(),
1185 .wordBreak = textStyle.GetWordBreak(),
1186 .ellipsisMode = textStyle.GetEllipsisMode(),
1187 .textOverflow = textStyle.GetTextOverflow(),
1188 };
1189 }
1190 } // namespace OHOS::Ace::NG
1191