• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.. All rights reserved.
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 "modules/skparagraph/src/ParagraphImpl.h"
17 #include "modules/skparagraph/src/TextTabAlign.h"
18 #include "log.h"
19 
20 namespace skia {
21 namespace textlayout {
22 
23 TextTabAlign::TextTabFuncs TextTabAlign::fTextTabFuncsTable[TextTabAlign::textAlignCount] = {
24     {
25         &TextTabAlign::leftAlignProcessTab,
26         &TextTabAlign::leftAlignProcessEndofWord,
27         &TextTabAlign::leftAlignProcessEndofLine,
28         &TextTabAlign::leftAlignProcessCluster
29     },
30     {
31         &TextTabAlign::rightAlignProcessTab,
32         &TextTabAlign::rightAlignProcessEndofWord,
33         &TextTabAlign::rightAlignProcessEndofLine,
34         &TextTabAlign::rightAlignProcessCluster
35     },
36     {
37         &TextTabAlign::centerAlignProcessTab,
38         &TextTabAlign::centerAlignProcessEndofWord,
39         &TextTabAlign::centerAlignProcessEndofLine,
40         &TextTabAlign::centerAlignProcessCluster
41     },
42 };
43 
init(SkScalar maxWidth,Cluster * endOfClusters)44 void TextTabAlign::init(SkScalar maxWidth, Cluster* endOfClusters)
45 {
46     fMaxWidth = maxWidth;
47     fEndOfClusters = endOfClusters;
48     if (fTabPosition < 1.0 || fTabAlignMode < TextAlign::kLeft || TextAlign::kCenter < fTabAlignMode ||
49         endOfClusters == nullptr) {
50         return;
51     }
52     fMaxTabIndex = fMaxWidth / fTabPosition;
53 
54     // If textAlign is configured, textTabAlign does not take effect
55     if (endOfClusters->getOwner()->paragraphStyle().getTextAlign() != TextAlign::kStart) {
56         TEXT_LOGD("textAlign is configured, textTabAlign does not take effect");
57         return;
58     }
59 
60     // If ellipsis is configured, textTabAlign does not take effect
61     if (endOfClusters->getOwner()->paragraphStyle().ellipsized()) {
62         TEXT_LOGD("ellipsis is configured, textTabAlign does not take effect");
63         return;
64     }
65 
66     TextAlign tabAlignMode = fTabAlignMode;
67     if (endOfClusters->getOwner()->paragraphStyle().getTextDirection() == TextDirection::kRtl) {
68         if (tabAlignMode == TextAlign::kLeft) {
69             tabAlignMode = TextAlign::kRight;
70         } else if (tabAlignMode == TextAlign::kRight) {
71             tabAlignMode = TextAlign::kLeft;
72         }
73     }
74     fTextTabFuncs = &(fTextTabFuncsTable[static_cast<size_t>(tabAlignMode)]);
75 }
76 
expendTabCluster(SkScalar width)77 void TextTabAlign::expendTabCluster(SkScalar width)
78 {
79     fTabCluster->run().extendClusterWidth(fTabCluster, width);
80     TEXT_LOGD("tabCluster(%zu, %zu) expend %f", fTabCluster->textRange().start, fTabCluster->textRange().end, width);
81 }
82 
leftAlignProcessTab(TextWrapper::TextStretch & words,TextWrapper::TextStretch & clusters,Cluster * currentCluster,SkScalar totalFakeSpacing)83 bool TextTabAlign::leftAlignProcessTab(TextWrapper::TextStretch& words, TextWrapper::TextStretch& clusters,
84     Cluster* currentCluster, SkScalar totalFakeSpacing)
85 {
86     fAlreadyInTab = true;
87     fTabCluster = currentCluster;
88     fTabBlockEnd = fTabCluster;
89     fTabStartPos = words.width() + clusters.width() + totalFakeSpacing;
90     do {
91         fTabIndex++;
92     } while ((fTabPosition * fTabIndex) < fTabStartPos);
93 
94     if (fTabIndex > fMaxTabIndex) {
95         expendTabCluster(0 - fTabCluster->width());
96         clusters.extend(currentCluster);
97         words.extend(clusters);
98         return true;
99     }
100 
101     fTabEndPos = fTabStartPos;
102     fTabShift = fTabPosition * fTabIndex - fTabStartPos;
103     expendTabCluster(fTabShift - fTabCluster->width());
104     return false;
105 }
106 
leftAlignProcessEndofWord(TextWrapper::TextStretch & words,TextWrapper::TextStretch & clusters,Cluster * currentCluster,SkScalar totalFakeSpacing)107 bool TextTabAlign::leftAlignProcessEndofWord(TextWrapper::TextStretch& words, TextWrapper::TextStretch& clusters,
108     Cluster* currentCluster, SkScalar totalFakeSpacing)
109 {
110     if (fAlreadyInTab) {
111         fTabBlockEnd = currentCluster;
112     }
113     return false;
114 }
115 
leftAlignProcessEndofLine(TextWrapper::TextStretch & words,TextWrapper::TextStretch & clusters,Cluster * currentCluster,SkScalar totalFakeSpacing)116 bool TextTabAlign::leftAlignProcessEndofLine(TextWrapper::TextStretch& words, TextWrapper::TextStretch& clusters,
117     Cluster* currentCluster, SkScalar totalFakeSpacing)
118 {
119     if (fAlreadyInTab && (fTabBlockEnd == fTabCluster)) {
120         words.shiftWidth(0 - fTabCluster->width());
121         expendTabCluster(0 - fTabCluster->width());
122     }
123     return false;
124 }
125 
leftAlignProcessCluster(TextWrapper::TextStretch & words,TextWrapper::TextStretch & clusters,Cluster * currentCluster,SkScalar totalFakeSpacing)126 bool TextTabAlign::leftAlignProcessCluster(TextWrapper::TextStretch& words, TextWrapper::TextStretch& clusters,
127     Cluster* currentCluster, SkScalar totalFakeSpacing)
128 {
129     if (fAlreadyInTab && (currentCluster->getOwner()->getWordBreakType() == WordBreakType::BREAK_ALL)) {
130         fTabBlockEnd = currentCluster;
131     }
132     return false;
133 }
134 
rightAlignProcessTabBlockEnd(TextWrapper::TextStretch & words,TextWrapper::TextStretch & clusters)135 void TextTabAlign::rightAlignProcessTabBlockEnd(TextWrapper::TextStretch& words, TextWrapper::TextStretch& clusters)
136 {
137     if ((fTabBlockEnd != fTabCluster) && ((fTabPosition * fTabIndex) > fTabEndPos)) {
138         fTabShift = fTabPosition * fTabIndex - fTabEndPos;
139         expendTabCluster(fTabShift);
140         words.shiftWidth(fTabShift);
141     }
142 }
143 
rightAlignProcessTab(TextWrapper::TextStretch & words,TextWrapper::TextStretch & clusters,Cluster * currentCluster,SkScalar totalFakeSpacing)144 bool TextTabAlign::rightAlignProcessTab(TextWrapper::TextStretch& words, TextWrapper::TextStretch& clusters,
145     Cluster* currentCluster, SkScalar totalFakeSpacing)
146 {
147     if (fAlreadyInTab) {
148         fTabBlockEnd = currentCluster;
149         fTabEndPos = words.width() + clusters.width() + totalFakeSpacing;
150         rightAlignProcessTabBlockEnd(words, clusters);
151     }
152 
153     fAlreadyInTab = true;
154     fTabCluster = currentCluster;
155     fTabBlockEnd = fTabCluster;
156     expendTabCluster(0 - fTabCluster->width());
157 
158     fTabStartPos = words.width() + clusters.width() + totalFakeSpacing;
159     do {
160         fTabIndex++;
161     } while ((fTabPosition * fTabIndex) < fTabStartPos);
162 
163     if (fTabIndex > fMaxTabIndex) {
164         clusters.extend(currentCluster);
165         words.extend(clusters);
166         return true;
167     }
168     fTabEndPos = fTabStartPos;
169     return false;
170 }
171 
rightAlignProcessEndofWord(TextWrapper::TextStretch & words,TextWrapper::TextStretch & clusters,Cluster * currentCluster,SkScalar totalFakeSpacing)172 bool TextTabAlign::rightAlignProcessEndofWord(TextWrapper::TextStretch& words, TextWrapper::TextStretch& clusters,
173     Cluster* currentCluster, SkScalar totalFakeSpacing)
174 {
175     if (!fAlreadyInTab) {
176         return false;
177     }
178 
179     fTabEndPos = words.width() + clusters.width() + totalFakeSpacing;
180     fTabBlockEnd = currentCluster;
181     if (currentCluster + 1 == fEndOfClusters) {
182         rightAlignProcessTabBlockEnd(words, clusters);
183         return false;
184     }
185 
186     if (currentCluster->isHardBreak()) {
187         fTabEndPos -= currentCluster->width();
188         return rightAlignProcessEndofLine(words, clusters, currentCluster, totalFakeSpacing);
189     }
190 
191     return false;
192 }
193 
rightAlignProcessEndofLine(TextWrapper::TextStretch & words,TextWrapper::TextStretch & clusters,Cluster * currentCluster,SkScalar totalFakeSpacing)194 bool TextTabAlign::rightAlignProcessEndofLine(TextWrapper::TextStretch& words, TextWrapper::TextStretch& clusters,
195     Cluster* currentCluster, SkScalar totalFakeSpacing)
196 {
197     if (!fAlreadyInTab) {
198         return false;
199     }
200 
201     rightAlignProcessTabBlockEnd(words, clusters);
202     return false;
203 }
204 
rightAlignProcessCluster(TextWrapper::TextStretch & words,TextWrapper::TextStretch & clusters,Cluster * currentCluster,SkScalar totalFakeSpacing)205 bool TextTabAlign::rightAlignProcessCluster(TextWrapper::TextStretch& words, TextWrapper::TextStretch& clusters,
206     Cluster* currentCluster, SkScalar totalFakeSpacing)
207 {
208     if (fAlreadyInTab && (currentCluster->getOwner()->getWordBreakType() == WordBreakType::BREAK_ALL)) {
209         fTabEndPos = words.width() + clusters.width() + totalFakeSpacing;
210         fTabBlockEnd = currentCluster;
211     }
212 
213     return false;
214 }
215 
centerAlignProcessTabBlockEnd(TextWrapper::TextStretch & words,TextWrapper::TextStretch & clusters)216 bool TextTabAlign::centerAlignProcessTabBlockEnd(TextWrapper::TextStretch& words, TextWrapper::TextStretch& clusters)
217 {
218     if ((fTabPosition * fTabIndex + ((fTabEndPos - fTabStartPos) / 2)) > fMaxWidth) {
219         return true;
220     }
221 
222     if ((fTabBlockEnd != fTabCluster) &&
223         ((fTabPosition * fTabIndex) > (fTabStartPos + ((fTabEndPos - fTabStartPos) / 2)))) {
224         fTabShift = fTabPosition * fTabIndex - (fTabStartPos + ((fTabEndPos - fTabStartPos) / 2));
225         expendTabCluster(fTabShift);
226         words.shiftWidth(fTabShift);
227     }
228     return false;
229 }
230 
centerAlignProcessTab(TextWrapper::TextStretch & words,TextWrapper::TextStretch & clusters,Cluster * currentCluster,SkScalar totalFakeSpacing)231 bool TextTabAlign::centerAlignProcessTab(TextWrapper::TextStretch& words, TextWrapper::TextStretch& clusters,
232     Cluster* currentCluster, SkScalar totalFakeSpacing)
233 {
234     if (fAlreadyInTab) {
235         fTabBlockEnd = currentCluster;
236         fTabEndPos = words.width() + clusters.width() + totalFakeSpacing;
237         if (centerAlignProcessTabBlockEnd(words, clusters)) {
238             clusters.extend(currentCluster);
239             return true;
240         }
241     }
242 
243     fAlreadyInTab = true;
244     fTabCluster = currentCluster;
245     fTabBlockEnd = fTabCluster;
246     expendTabCluster(0 - fTabCluster->width());
247 
248     fTabStartPos = words.width() + clusters.width() + totalFakeSpacing;
249     do {
250         fTabIndex++;
251     } while ((fTabPosition * fTabIndex) < fTabStartPos);
252 
253     if (fTabIndex > fMaxTabIndex) {
254         clusters.extend(currentCluster);
255         words.extend(clusters);
256         return true;
257     }
258 
259     fTabEndPos = fTabStartPos;
260     return false;
261 }
262 
centerAlignProcessEndofWord(TextWrapper::TextStretch & words,TextWrapper::TextStretch & clusters,Cluster * currentCluster,SkScalar totalFakeSpacing)263 bool TextTabAlign::centerAlignProcessEndofWord(TextWrapper::TextStretch& words, TextWrapper::TextStretch& clusters,
264     Cluster* currentCluster, SkScalar totalFakeSpacing)
265 {
266     if (!fAlreadyInTab) {
267         return false;
268     }
269 
270     SkScalar tabEndPosTmp = words.width() + clusters.width() + totalFakeSpacing;
271     if ((fTabPosition * fTabIndex + ((tabEndPosTmp - fTabStartPos) / 2)) > fMaxWidth) {
272         centerAlignProcessTabBlockEnd(words, clusters);
273         return true;
274     }
275 
276     fTabEndPos = tabEndPosTmp;
277     fTabBlockEnd = currentCluster;
278 
279     if (currentCluster + 1 == fEndOfClusters) {
280         return centerAlignProcessTabBlockEnd(words, clusters);
281     }
282 
283     if (currentCluster->isHardBreak()) {
284         fTabEndPos -= currentCluster->width();
285         return centerAlignProcessEndofLine(words, clusters, currentCluster, totalFakeSpacing);
286     }
287 
288     return false;
289 }
290 
centerAlignProcessEndofLine(TextWrapper::TextStretch & words,TextWrapper::TextStretch & clusters,Cluster * currentCluster,SkScalar totalFakeSpacing)291 bool TextTabAlign::centerAlignProcessEndofLine(TextWrapper::TextStretch& words, TextWrapper::TextStretch& clusters,
292     Cluster* currentCluster, SkScalar totalFakeSpacing)
293 {
294     if (!fAlreadyInTab) {
295         return false;
296     }
297 
298     centerAlignProcessTabBlockEnd(words, clusters);
299     return false;
300 }
301 
centerAlignProcessCluster(TextWrapper::TextStretch & words,TextWrapper::TextStretch & clusters,Cluster * currentCluster,SkScalar totalFakeSpacing)302 bool TextTabAlign::centerAlignProcessCluster(TextWrapper::TextStretch& words, TextWrapper::TextStretch& clusters,
303     Cluster* currentCluster, SkScalar totalFakeSpacing)
304 {
305     if ((!fAlreadyInTab) || (currentCluster->getOwner()->getWordBreakType() != WordBreakType::BREAK_ALL)) {
306         return false;
307     }
308 
309     SkScalar tabEndPosTmp = words.width() + clusters.width() + totalFakeSpacing;
310     if (((tabEndPosTmp - fTabStartPos) / 2) > (fMaxWidth - fTabPosition * fTabIndex)) {
311         centerAlignProcessTabBlockEnd(words, clusters);
312         return true;
313     }
314 
315     fTabEndPos = tabEndPosTmp;
316     fTabBlockEnd = currentCluster;
317     return false;
318 }
319 
320 }  // namespace textlayout
321 }  // namespace skia