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