• 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 #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