• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 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/paragraph_util.h"
17 
18 #include "text_layout_adapter.h"
19 
20 #include "base/memory/ace_type.h"
21 #include "core/components_ng/pattern/text/text_layout_property.h"
22 
23 namespace OHOS::Ace::NG {
GetParagraphStyle(const TextStyle & textStyle)24 ParagraphStyle ParagraphUtil::GetParagraphStyle(const TextStyle& textStyle)
25 {
26     return { .direction = textStyle.GetTextDirection(),
27         .align = textStyle.GetTextAlign(),
28         .verticalAlign = textStyle.GetParagraphVerticalAlign(),
29         .maxLines = static_cast<int32_t>(textStyle.GetMaxLines()) < 0 ? UINT32_MAX : textStyle.GetMaxLines(),
30         .fontLocale = textStyle.GetLocale(),
31         .wordBreak = textStyle.GetWordBreak(),
32         .ellipsisMode = textStyle.GetEllipsisMode(),
33         .lineBreakStrategy = textStyle.GetLineBreakStrategy(),
34         .textOverflow = textStyle.GetTextOverflow(),
35         .indent = textStyle.GetTextIndent(),
36         .halfLeading = textStyle.GetHalfLeading(),
37         .paragraphSpacing = textStyle.GetParagraphSpacing(),
38         .isOnlyBetweenLines = textStyle.GetIsOnlyBetweenLines(),
39         .optimizeTrailingSpace = textStyle.GetOptimizeTrailingSpace(),
40         .enableAutoSpacing = textStyle.GetEnableAutoSpacing()
41         };
42 }
43 
GetTextDirection(const std::u16string & content,LayoutWrapper * layoutWrapper)44 TextDirection ParagraphUtil::GetTextDirection(
45     const std::u16string& content, LayoutWrapper* layoutWrapper)
46 {
47     if (!layoutWrapper) {
48         return GetTextDirectionByContent(content);
49     }
50     auto textLayoutProperty = AceType::DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
51     CHECK_NULL_RETURN(textLayoutProperty, TextDirection::LTR);
52 
53     auto direction = textLayoutProperty->GetLayoutDirection();
54     if (direction == TextDirection::LTR || direction == TextDirection::RTL) {
55         return direction;
56     }
57     return GetTextDirectionByContent(content);
58 }
59 
GetTextDirectionByContent(const std::u16string & content)60 TextDirection ParagraphUtil::GetTextDirectionByContent(const std::u16string& content)
61 {
62     bool isRTL = AceApplicationInfo::GetInstance().IsRightToLeft();
63     auto textDirection = isRTL ? TextDirection::RTL : TextDirection::LTR;
64     for (const auto& charOfShowingText : content) {
65         if (TextLayoutadapter::IsLeftToRight(charOfShowingText)) {
66             return TextDirection::LTR;
67         } else if (TextLayoutadapter::IsRightToLeft(charOfShowingText)) {
68             return TextDirection::RTL;
69         } else if (TextLayoutadapter::IsRightTOLeftArabic(charOfShowingText)) {
70             return TextDirection::RTL;
71         }
72     }
73     return textDirection;
74 }
75 
GetSpanParagraphStyle(LayoutWrapper * layoutWrapper,const RefPtr<SpanItem> & spanItem,ParagraphStyle & pStyle)76 void ParagraphUtil::GetSpanParagraphStyle(
77     LayoutWrapper* layoutWrapper, const RefPtr<SpanItem>& spanItem, ParagraphStyle& pStyle)
78 {
79     const auto& lineStyle = spanItem->textLineStyle;
80     CHECK_NULL_VOID(lineStyle);
81     if (lineStyle->HasTextAlign()) {
82         pStyle.align = lineStyle->GetTextAlignValue();
83     }
84     if (lineStyle->HasTextVerticalAlign()) {
85         pStyle.verticalAlign = lineStyle->GetTextVerticalAlignValue();
86     }
87     if (lineStyle->HasMaxLines()) {
88         pStyle.maxLines = std::min(lineStyle->GetMaxLinesValue(), pStyle.maxLines);
89     }
90     if (lineStyle->HasWordBreak()) {
91         pStyle.wordBreak = lineStyle->GetWordBreakValue();
92     }
93     if (lineStyle->HasEllipsisMode()) {
94         pStyle.ellipsisMode = lineStyle->GetEllipsisModeValue();
95     }
96     if (lineStyle->HasTextOverflow()) {
97         pStyle.textOverflow = lineStyle->GetTextOverflowValue();
98     }
99     if (lineStyle->HasTextIndent()) {
100         pStyle.indent = lineStyle->GetTextIndentValue();
101     }
102     if (lineStyle->HasLineBreakStrategy()) {
103         pStyle.lineBreakStrategy = lineStyle->GetLineBreakStrategyValue();
104     }
105     if (lineStyle->HasLeadingMargin()) {
106         pStyle.leadingMargin = lineStyle->GetLeadingMarginValue();
107     }
108     if (lineStyle->HasLineHeight()) {
109         pStyle.lineHeight = lineStyle->GetLineHeightValue();
110     }
111     if (lineStyle->HasHalfLeading()) {
112         pStyle.halfLeading = lineStyle->GetHalfLeadingValue();
113     }
114     if (lineStyle->HasParagraphSpacing()) {
115         pStyle.paragraphSpacing = lineStyle->GetParagraphSpacingValue();
116     }
117     pStyle.direction = GetTextDirection(spanItem->content, layoutWrapper);
118 }
119 
ConstructParagraphSpanGroup(std::list<RefPtr<SpanItem>> & spans,std::vector<std::list<RefPtr<SpanItem>>> & spanGroupVec,bool & spanStringHasMaxLines)120 void ParagraphUtil::ConstructParagraphSpanGroup(std::list<RefPtr<SpanItem>>& spans,
121     std::vector<std::list<RefPtr<SpanItem>>>& spanGroupVec,
122     bool& spanStringHasMaxLines)
123 {
124     // split spans into groups by mew paragraph style
125     auto it = spans.begin();
126     ParagraphStyle pStyle;
127     GetSpanParagraphStyle(nullptr, (*it), pStyle);
128     while (it != spans.end()) {
129         auto spanItem = *it;
130         if (!spanItem) {
131             ++it;
132             continue;
133         }
134         spanItem->SetNeedRemoveNewLine(false);
135         if (spanItem->content.back() == u'\n') {
136             if (std::next(it) == spans.end()) {
137                 break;
138             }
139             auto next = *(std::next(it));
140             ParagraphStyle nextSpanParagraphStyle;
141             if (next) {
142                 GetSpanParagraphStyle(nullptr, next, nextSpanParagraphStyle);
143             } else {
144                 break;
145             }
146             if (pStyle != nextSpanParagraphStyle ||
147                 (pStyle.leadingMargin.has_value() && pStyle.leadingMargin->pixmap) || Positive(pStyle.indent.Value()) ||
148                 pStyle.maxLines != UINT32_MAX) {
149                 std::list<RefPtr<SpanItem>> newGroup;
150                 spanItem->SetNeedRemoveNewLine(true);
151                 newGroup.splice(newGroup.begin(), spans, spans.begin(), std::next(it));
152                 spanStringHasMaxLines |= pStyle.maxLines != UINT32_MAX;
153                 spanGroupVec.emplace_back(std::move(newGroup));
154                 it = spans.begin();
155                 pStyle = nextSpanParagraphStyle;
156                 continue;
157             }
158         }
159         ++it;
160     }
161     if (!spans.empty()) {
162         auto maxlines = spans.front()->textLineStyle->GetMaxLines().value_or(UINT32_MAX);
163         spanStringHasMaxLines |= maxlines != UINT32_MAX;
164         spanGroupVec.emplace_back(std::move(spans));
165     }
166 }
167 
HandleEmptyParagraph(RefPtr<Paragraph> paragraph,const std::list<RefPtr<SpanItem>> & spanGroup)168 void ParagraphUtil::HandleEmptyParagraph(RefPtr<Paragraph> paragraph,
169     const std::list<RefPtr<SpanItem>>& spanGroup)
170 {
171     CHECK_NULL_VOID(paragraph && spanGroup.size() == 1);
172     auto spanItem = spanGroup.front();
173     CHECK_NULL_VOID(spanItem);
174     auto content = spanItem->GetSpanContent(spanItem->GetSpanContent());
175     CHECK_NULL_VOID(content.empty());
176     auto textStyle = spanItem->GetTextStyle();
177     CHECK_NULL_VOID(textStyle.has_value());
178     paragraph->PushStyle(textStyle.value());
179 }
180 
ApplyIndent(ParagraphStyle & paragraphStyle,const RefPtr<Paragraph> & paragraph,double width,const TextStyle & textStyle,double indentMaxWidth)181 void ParagraphUtil::ApplyIndent(
182     ParagraphStyle& paragraphStyle, const RefPtr<Paragraph>& paragraph, double width, const TextStyle& textStyle,
183     double indentMaxWidth)
184 {
185     auto indentValue = paragraphStyle.indent;
186     CHECK_NULL_VOID(paragraph);
187     double value = 0.0;
188     if (GreatNotEqual(indentValue.Value(), 0.0)) {
189         // first line indent
190         auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
191         CHECK_NULL_VOID(pipeline);
192         if (indentValue.Unit() != DimensionUnit::PERCENT) {
193             value = indentValue.ConvertToPxDistribute(
194                 textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
195         } else {
196             value = static_cast<float>(indentMaxWidth * indentValue.Value());
197             paragraphStyle.indent = Dimension(value);
198         }
199     }
200     auto indent = static_cast<float>(value);
201     auto leadingMarginValue = 0.0f;
202     std::vector<float> indents;
203     if (paragraphStyle.leadingMargin.has_value()) {
204         leadingMarginValue = paragraphStyle.leadingMargin->size.Width().ConvertToPxDistribute(
205             textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
206     }
207     indents.emplace_back(indent + leadingMarginValue);
208     indents.emplace_back(leadingMarginValue);
209     paragraph->SetIndents(indents);
210 }
211 
CreateImageSourceInfo(const ImageSpanOptions & options)212 ImageSourceInfo ParagraphUtil::CreateImageSourceInfo(const ImageSpanOptions& options)
213 {
214     std::string src;
215     RefPtr<PixelMap> pixMap = nullptr;
216     std::string bundleName;
217     std::string moduleName;
218     if (options.image.has_value()) {
219         src = options.image.value();
220     }
221     if (options.imagePixelMap.has_value()) {
222         pixMap = options.imagePixelMap.value();
223     }
224     if (options.bundleName.has_value()) {
225         bundleName = options.bundleName.value();
226     }
227     if (options.moduleName.has_value()) {
228         moduleName = options.moduleName.value();
229     }
230     ImageSourceInfo info;
231 #if defined(PIXEL_MAP_SUPPORTED)
232     if (!options.imagePixelMap.has_value()) {
233         info = ImageSourceInfo{ src, bundleName, moduleName };
234     } else {
235         info = ImageSourceInfo(pixMap);
236     }
237 #else
238     info = ImageSourceInfo{ src, bundleName, moduleName };
239 #endif
240     info.SetIsUriPureNumber(options.isUriPureNumber.value_or(false));
241     return info;
242 }
243 } // namespace OHOS::Ace::NG