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