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