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 "base/utils/measure_util.h"
17 #include "core/components/common/properties/text_style.h"
18 #include "frameworks/core/components/font/constants_converter.h"
19 #include "frameworks/core/components/font/rosen_font_collection.h"
20 #include "frameworks/core/components/text/text_theme.h"
21 #include "rosen_text/text_style.h"
22 #include "rosen_text/typography.h"
23 #include "rosen_text/typography_create.h"
24 #include "ui/base/utils/utils.h"
25
26 namespace OHOS::Ace {
27 namespace {
28 const std::u16string ELLIPSIS = u"\u2026";
29 const std::string FONTWEIGHT = "wght";
30
ApplyLineHeightInNumUnit(const MeasureContext & context,Rosen::TextStyle & txtStyle)31 void ApplyLineHeightInNumUnit(const MeasureContext& context, Rosen::TextStyle& txtStyle)
32 {
33 auto lineHeight = context.lineHeight.value().ConvertToPx();
34 txtStyle.heightOnly = true;
35 if (!NearEqual(lineHeight, txtStyle.fontSize) && (lineHeight > 0.0) && (!NearZero(txtStyle.fontSize))) {
36 txtStyle.heightScale = lineHeight / txtStyle.fontSize;
37 } else {
38 txtStyle.heightScale = 1;
39 static const int32_t BEGIN_VERSION = 6;
40 auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
41 auto isBeginVersion = pipelineContext && pipelineContext->GetMinPlatformVersion() >= BEGIN_VERSION;
42 if (NearZero(lineHeight) || (!isBeginVersion && NearEqual(lineHeight, txtStyle.fontSize))) {
43 txtStyle.heightOnly = false;
44 }
45 }
46 }
47
IsApplyIndent(const MeasureContext & context,double & indent)48 bool IsApplyIndent(const MeasureContext& context, double& indent)
49 {
50 auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
51 CHECK_NULL_RETURN(pipeline, false);
52 if (context.textIndent.value().Unit() != DimensionUnit::PERCENT) {
53 indent = context.textIndent.value().ConvertToPx();
54 return true;
55 } else {
56 if (context.constraintWidth.has_value()) {
57 indent = context.constraintWidth.value().ConvertToPx() * context.textIndent.value().Value();
58 return true;
59 }
60 }
61 return false;
62 }
63
prepareTextStyleForMeasure(const MeasureContext & context)64 Rosen::TextStyle prepareTextStyleForMeasure(const MeasureContext& context)
65 {
66 using namespace Constants;
67 Rosen::TextStyle txtStyle;
68 std::vector<std::string> fontFamilies;
69 if (context.fontSize.has_value()) {
70 txtStyle.fontSize = context.fontSize.value().ConvertToPx();
71 } else {
72 auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
73 CHECK_NULL_RETURN(pipelineContext, txtStyle);
74 auto textTheme = pipelineContext->GetTheme<TextTheme>();
75 txtStyle.fontSize = textTheme->GetTextStyle().GetFontSize().ConvertToPx();
76 }
77 txtStyle.fontStyle = ConvertTxtFontStyle(context.fontStyle);
78 FontWeight fontWeightStr = StringUtils::StringToFontWeight(context.fontWeight);
79 txtStyle.fontWeight = ConvertTxtFontWeight(fontWeightStr);
80 auto fontWeightValue = (static_cast<int32_t>(
81 ConvertTxtFontWeight(fontWeightStr)) + 1) * 100; // 100: coefficient for transformation
82 auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
83 if (pipelineContext) {
84 fontWeightValue = fontWeightValue * pipelineContext->GetFontWeightScale();
85 }
86 txtStyle.fontVariations.SetAxisValue(FONTWEIGHT, fontWeightValue);
87 StringUtils::StringSplitter(context.fontFamily, ',', fontFamilies);
88 txtStyle.fontFamilies = fontFamilies;
89 if (context.letterSpacing.has_value()) {
90 txtStyle.letterSpacing = context.letterSpacing.value().ConvertToPx();
91 }
92 if (context.lineHeight.has_value()) {
93 if (context.lineHeight->Unit() == DimensionUnit::PERCENT) {
94 txtStyle.heightOnly = true;
95 txtStyle.heightScale = context.lineHeight->Value();
96 } else {
97 ApplyLineHeightInNumUnit(context, txtStyle);
98 }
99 }
100 return txtStyle;
101 }
102
prepareParagraphForMeasure(const MeasureContext & context,std::unique_ptr<Rosen::Typography> & paragraph)103 void prepareParagraphForMeasure(const MeasureContext& context, std::unique_ptr<Rosen::Typography>& paragraph)
104 {
105 if (context.textIndent.has_value() && !LessOrEqual(context.textIndent.value().Value(), 0.0)) {
106 double indent = 0.0;
107 // first line indent
108 if (IsApplyIndent(context, indent)) {
109 std::vector<float> indents;
110 // only indent first line
111 indents.emplace_back(static_cast<float>(indent));
112 indents.emplace_back(0.0);
113 paragraph->SetIndents(indents);
114 }
115 }
116 if (context.constraintWidth.has_value()) {
117 paragraph->Layout(context.constraintWidth.value().ConvertToPx());
118 } else {
119 paragraph->Layout(Size::INFINITE_SIZE);
120 }
121 }
122
MeasureTextInner(const MeasureContext & context)123 double MeasureTextInner(const MeasureContext& context)
124 {
125 using namespace Constants;
126 Rosen::TypographyStyle style;
127 auto fontCollection = RosenFontCollection::GetInstance().GetFontCollection();
128 if (!fontCollection) {
129 LOGW("fontCollection is null");
130 return 0.0;
131 }
132 std::unique_ptr<Rosen::TypographyCreate> builder = Rosen::TypographyCreate::Create(style, fontCollection);
133 Rosen::TextStyle txtStyle;
134 std::vector<std::string> fontFamilies;
135 if (context.fontSize) {
136 txtStyle.fontSize = context.fontSize.value().ConvertToPx();
137 } else {
138 auto pipelineContext = PipelineBase::GetCurrentContext();
139 CHECK_NULL_RETURN(pipelineContext, 0.0);
140 auto textTheme = pipelineContext->GetTheme<TextTheme>();
141 txtStyle.fontSize = textTheme->GetTextStyle().GetFontSize().ConvertToPx();
142 }
143 txtStyle.fontStyle = ConvertTxtFontStyle(context.fontStyle);
144 FontWeight fontWeightStr = StringUtils::StringToFontWeight(context.fontWeight);
145 txtStyle.fontWeight = ConvertTxtFontWeight(fontWeightStr);
146 auto fontWeightValue = (static_cast<int32_t>(
147 ConvertTxtFontWeight(fontWeightStr)) + 1) * 100; // 100: coefficient for transformation
148 auto pipelineContext = PipelineBase::GetCurrentContextSafely();
149 if (pipelineContext) {
150 fontWeightValue = fontWeightValue * pipelineContext->GetFontWeightScale();
151 }
152 txtStyle.fontVariations.SetAxisValue(FONTWEIGHT, fontWeightValue);
153 StringUtils::StringSplitter(context.fontFamily, ',', fontFamilies);
154 txtStyle.fontFamilies = fontFamilies;
155 if (context.letterSpacing.has_value()) {
156 txtStyle.letterSpacing = context.letterSpacing.value().ConvertToPx();
157 }
158
159 builder->PushStyle(txtStyle);
160 builder->AppendText(StringUtils::Str8ToStr16(context.textContent));
161 auto paragraph = builder->CreateTypography();
162 if (!paragraph) {
163 return 0.0;
164 }
165 paragraph->Layout(Size::INFINITE_SIZE);
166 return std::ceil(paragraph->GetActualWidth());
167 }
168
MeasureTextSizeInner(const MeasureContext & context)169 Size MeasureTextSizeInner(const MeasureContext& context)
170 {
171 using namespace Constants;
172 auto fontCollection = RosenFontCollection::GetInstance().GetFontCollection();
173 if (!fontCollection) {
174 TAG_LOGW(AceLogTag::ACE_FONT, "fontCollection is null");
175 return Size(0.0, 0.0);
176 }
177 ACE_TEXT_SCOPED_TRACE("MeasureTextSizeInner");
178 Rosen::TypographyStyle style;
179 style.textAlign = ConvertTxtTextAlign(context.textAlign);
180 if (context.textOverlayFlow == TextOverflow::ELLIPSIS) {
181 style.ellipsis = ELLIPSIS;
182 }
183 if (GreatNotEqual(context.maxlines, 0.0)) {
184 style.maxLines = context.maxlines;
185 }
186 style.wordBreakType = static_cast<Rosen::WordBreakType>(context.wordBreak);
187 std::unique_ptr<Rosen::TypographyCreate> builder = Rosen::TypographyCreate::Create(style, fontCollection);
188
189 Rosen::TextStyle txtStyle = prepareTextStyleForMeasure(context);
190 builder->PushStyle(txtStyle);
191 std::string content = context.textContent;
192 StringUtils::TransformStrCase(content, static_cast<int32_t>(context.textCase));
193 builder->AppendText(StringUtils::Str8ToStr16(content));
194
195 auto paragraph = builder->CreateTypography();
196 CHECK_NULL_RETURN(paragraph, Size(0.0, 0.0));
197
198 prepareParagraphForMeasure(context, paragraph);
199 double textWidth = 0.0;
200 auto* paragraphTxt = static_cast<Rosen::Typography*>(paragraph.get());
201 if (paragraphTxt->GetLineCount() == 1 && !context.isReturnActualWidth) {
202 textWidth = std::max(paragraph->GetActualWidth(), paragraph->GetMaxIntrinsicWidth());
203 } else {
204 textWidth = paragraph->GetActualWidth();
205 }
206 auto sizeWidth = std::min(paragraph->GetMaxWidth(), textWidth);
207 sizeWidth =
208 context.constraintWidth.has_value() ? context.constraintWidth.value().ConvertToPx() : std::ceil(sizeWidth);
209
210 float baselineOffset = 0.0;
211 if (context.baselineOffset.has_value()) {
212 baselineOffset = static_cast<float>(context.baselineOffset.value().ConvertToPx());
213 }
214 float heightFinal = static_cast<float>(paragraph->GetHeight()) + std::fabs(baselineOffset);
215 if (SystemProperties::GetDebugEnabled()) {
216 TAG_LOGI(AceLogTag::ACE_FONT, "MeasureTextSize [maxW:%{public}f, H:%{public}f],[w:%{public}f, h:%{public}f]",
217 paragraph->GetMaxWidth(), paragraph->GetHeight(), sizeWidth, heightFinal);
218 }
219
220 return Size(sizeWidth, heightFinal);
221 }
222 }
223
MeasureText(const MeasureContext & context)224 double MeasureUtil::MeasureText(const MeasureContext& context)
225 {
226 if (SystemProperties::GetRosenBackendEnabled()) {
227 #ifdef ENABLE_ROSEN_BACKEND
228 return MeasureTextInner(context);
229 #else
230 return 0.0;
231 #endif
232 } else {
233 return 0.0;
234 }
235 }
236
MeasureTextSize(const MeasureContext & context)237 Size MeasureUtil::MeasureTextSize(const MeasureContext& context)
238 {
239 if (SystemProperties::GetRosenBackendEnabled()) {
240 #ifdef ENABLE_ROSEN_BACKEND
241 return MeasureTextSizeInner(context);
242 #else
243 return Size(0.0, 0.0);
244 #endif
245 } else {
246 return Size(0.0, 0.0);
247 }
248 }
249
MeasureTextSize(const TextStyle & textStyle,const std::string & text)250 Size MeasureUtil::MeasureTextSize(const TextStyle& textStyle, const std::string& text)
251 {
252 #ifdef ENABLE_ROSEN_BACKEND
253 MeasureContext content;
254 content.textContent = text;
255 content.fontSize = textStyle.GetFontSize();
256 auto fontweight = StringUtils::FontWeightToString(textStyle.GetFontWeight());
257 content.fontWeight = fontweight;
258 content.isReturnActualWidth = true;
259 content.maxlines = 1;
260 return MeasureTextSizeInner(content);
261 #else
262 return 0.0f;
263 #endif
264 }
265
MeasureTextWidth(const TextStyle & textStyle,const std::string & text)266 double MeasureUtil::MeasureTextWidth(const TextStyle& textStyle, const std::string& text)
267 {
268 auto width = MeasureTextSize(textStyle, text).Width();
269 return std::max(width, 0.0);
270 }
271 } // namespace OHOS::Ace
272