1 /*
2  * Copyright (c) 2024 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_adapt_font_sizer.h"
17 
18 #include <limits>
19 
20 #include "base/geometry/dimension.h"
21 #include "base/utils/utils.h"
22 #include "core/common/container.h"
23 #include "core/common/font_manager.h"
24 #include "core/pipeline_ng/pipeline_context.h"
25 
26 namespace OHOS::Ace::NG {
AdaptMaxFontSize(TextStyle & textStyle,const std::string & content,const Dimension & stepUnit,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)27 bool TextAdaptFontSizer::AdaptMaxFontSize(TextStyle& textStyle, const std::string& content,
28     const Dimension& stepUnit, const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
29 {
30     double maxFontSize = 0.0;
31     double minFontSize = 0.0;
32     GetAdaptMaxMinFontSize(textStyle, maxFontSize, minFontSize, contentConstraint);
33     if (LessNotEqual(maxFontSize, minFontSize) || LessOrEqual(minFontSize, 0.0)) {
34         // minFontSize or maxFontSize is invalid
35         return CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper, false);
36     }
37     double stepSize = 0.0;
38     GetAdaptFontSizeStep(textStyle, stepSize, stepUnit, contentConstraint);
39     auto maxSize = GetMaxMeasureSize(contentConstraint);
40     GetSuitableSize(maxSize, layoutWrapper);
41     // Use the minFontSize to layout the paragraph. While using the minFontSize, if the paragraph could be layout in 1
42     // line, then increase the font size and try to layout using the maximum available fontsize.
43     textStyle.SetFontSize(Dimension(minFontSize));
44     CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper);
45     if (IsAdaptExceedLimit(maxSize)) {
46         return true;
47     }
48     auto tag = static_cast<int32_t>((maxFontSize - minFontSize) / stepSize);
49     auto length = tag + 1 + (GreatNotEqual(maxFontSize, minFontSize + stepSize * tag) ? 1 : 0);
50     int32_t left = 0;
51     int32_t right = length - 1;
52     float fontSize = 0.0f;
53     while (left <= right) {
54         int32_t mid = left + (right - left) / 2;
55         fontSize = static_cast<float>((mid == length - 1) ? (maxFontSize) : (minFontSize + stepSize * mid));
56         textStyle.SetFontSize(Dimension(fontSize));
57         if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) {
58             return false;
59         }
60         if (!IsAdaptExceedLimit(maxSize)) {
61             left = mid + 1;
62         } else {
63             right = mid - 1;
64         }
65     }
66     fontSize = static_cast<float>((left - 1 == length - 1) ? (maxFontSize) : (minFontSize + stepSize * (left - 1)));
67     fontSize = LessNotEqual(fontSize, minFontSize) ? minFontSize : fontSize;
68     fontSize = GreatNotEqual(fontSize, maxFontSize) ? maxFontSize : fontSize;
69     textStyle.SetFontSize(Dimension(fontSize));
70     return CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper);
71 }
72 
AdaptMinFontSize(TextStyle & textStyle,const std::string & content,const Dimension & stepUnit,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)73 bool TextAdaptFontSizer::AdaptMinFontSize(TextStyle& textStyle, const std::string& content,
74     const Dimension& stepUnit, const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
75 {
76     double maxFontSize = 0.0;
77     double minFontSize = 0.0;
78     GetAdaptMaxMinFontSize(textStyle, maxFontSize, minFontSize, contentConstraint);
79     if (LessNotEqual(maxFontSize, minFontSize) || LessOrEqual(minFontSize, 0.0)) {
80         return CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper, false);
81     }
82     double stepSize = 0.0;
83     GetAdaptFontSizeStep(textStyle, stepSize, stepUnit, contentConstraint);
84     auto maxSize = GetMaxMeasureSize(contentConstraint);
85     GetSuitableSize(maxSize, layoutWrapper);
86     while (GreatOrEqual(maxFontSize, minFontSize)) {
87         textStyle.SetFontSize(Dimension(maxFontSize));
88         if (!CreateParagraphAndLayout(textStyle, content, contentConstraint, layoutWrapper)) {
89             return false;
90         }
91         if (!DidExceedMaxLines(maxSize)) {
92             break;
93         }
94         maxFontSize -= stepSize;
95     }
96     return true;
97 }
98 
GetAdaptMaxMinFontSize(const TextStyle & textStyle,double & maxFontSize,double & minFontSize,const LayoutConstraintF & contentConstraint)99 void TextAdaptFontSizer::GetAdaptMaxMinFontSize(const TextStyle& textStyle, double& maxFontSize, double& minFontSize,
100     const LayoutConstraintF& contentConstraint)
101 {
102     maxFontSize = textStyle.GetAdaptMaxFontSize().ConvertToPxDistribute(
103         textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
104     minFontSize = textStyle.GetAdaptMinFontSize().ConvertToPxDistribute(
105         textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
106 }
107 
GetAdaptFontSizeStep(const TextStyle & textStyle,double & stepSize,const Dimension & stepUnit,const LayoutConstraintF & contentConstraint)108 void TextAdaptFontSizer::GetAdaptFontSizeStep(const TextStyle& textStyle, double& stepSize, const Dimension& stepUnit,
109     const LayoutConstraintF& contentConstraint)
110 {
111     Dimension step = stepUnit;
112     if (GreatNotEqual(textStyle.GetAdaptFontSizeStep().Value(), 0.0)) {
113         step = textStyle.GetAdaptFontSizeStep();
114     }
115     stepSize =
116         step.ConvertToPxDistribute(textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
117 }
118 
GetMaxMeasureSize(const LayoutConstraintF & contentConstraint)119 SizeF TextAdaptFontSizer::GetMaxMeasureSize(const LayoutConstraintF& contentConstraint)
120 {
121     auto maxSize = contentConstraint.selfIdealSize;
122     maxSize.UpdateIllegalSizeWithCheck(contentConstraint.maxSize);
123     return maxSize.ConvertToSizeT();
124 }
125 
DidExceedMaxLines(const SizeF & maxSize)126 bool TextAdaptFontSizer::DidExceedMaxLines(const SizeF& maxSize)
127 {
128     auto paragraph = GetParagraph();
129     CHECK_NULL_RETURN(paragraph, false);
130     bool didExceedMaxLines = paragraph->DidExceedMaxLines();
131     didExceedMaxLines = didExceedMaxLines || GreatNotEqual(paragraph->GetHeight(), maxSize.Height());
132     didExceedMaxLines = didExceedMaxLines || GreatNotEqual(paragraph->GetLongestLine(), maxSize.Width());
133     return didExceedMaxLines;
134 }
135 
IsAdaptExceedLimit(const SizeF & maxSize)136 bool TextAdaptFontSizer::IsAdaptExceedLimit(const SizeF& maxSize)
137 {
138     auto paragraph = GetParagraph();
139     CHECK_NULL_RETURN(paragraph, false);
140     return (paragraph->GetLineCount() > 1) || paragraph->DidExceedMaxLines() ||
141         GreatNotEqual(paragraph->GetLongestLine(), maxSize.Width());
142 }
143 
IsNeedAdaptFontSize(const double & maxFontSize,const double & minFontSize)144 bool TextAdaptFontSizer::IsNeedAdaptFontSize(const double& maxFontSize, const double& minFontSize)
145 {
146     if (LessNotEqual(maxFontSize, minFontSize) || LessOrEqual(minFontSize, 0.0)) {
147         return false;
148     }
149     return true;
150 }
151 
IsNeedAdaptFontSize(const TextStyle & textStyle,const LayoutConstraintF & contentConstraint)152 bool TextAdaptFontSizer::IsNeedAdaptFontSize(const TextStyle& textStyle, const LayoutConstraintF& contentConstraint)
153 {
154     double maxFontSize = 0.0;
155     double minFontSize = 0.0;
156     GetAdaptMaxMinFontSize(textStyle, maxFontSize, minFontSize, contentConstraint);
157     return IsNeedAdaptFontSize(maxFontSize, minFontSize);
158 }
159 
SetAdaptFontSizeLineHeight(const Dimension & lineHeight,const TextStyle & textStyle)160 void TextAdaptFontSizer::SetAdaptFontSizeLineHeight(const Dimension& lineHeight, const TextStyle& textStyle)
161 {
162     lineHeight_ = lineHeight.ConvertToPxDistribute(
163         textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
164 }
165 
IsAdaptFontSizeExceedLineHeight(const RefPtr<Paragraph> & paragraph)166 bool TextAdaptFontSizer::IsAdaptFontSizeExceedLineHeight(const RefPtr<Paragraph>& paragraph)
167 {
168     if (LessOrEqual(lineHeight_, 0.0)) {
169         return false;
170     }
171     auto lineMetrics = paragraph->GetLineMetrics(0);
172     return GreatNotEqual(lineMetrics.height, lineHeight_);
173 }
174 } // namespace OHOS::Ace::NG
175