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