• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
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 "shaper.h"
17 
18 #include <queue>
19 #include <variant>
20 
21 #include "bidi_processer.h"
22 #include "line_breaker.h"
23 #include "measurer.h"
24 #include "texgine/any_span.h"
25 #include "texgine_exception.h"
26 #include "texgine/utils/exlog.h"
27 #ifdef LOGGER_ENABLE_SCOPE
28 #include "texgine/utils/trace.h"
29 #endif
30 #include "text_breaker.h"
31 #include "text_merger.h"
32 #include "text_reverser.h"
33 #include "text_shaper.h"
34 
35 namespace OHOS {
36 namespace Rosen {
37 namespace TextEngine {
38 #define MAXWIDTH 1e9
39 #define TEXTOVERFLOWER 1
40 
41 namespace {
DumpLineMetrics(const std::vector<LineMetrics> & lineMetrics)42 void DumpLineMetrics(const std::vector<LineMetrics> &lineMetrics)
43 {
44     LOGSCOPED(sl, LOGEX_FUNC_LINE_DEBUG(), "DumpLineMetrics");
45     for (const auto &metric : lineMetrics) {
46         for (const auto &span : metric.lineSpans) {
47             span.Dump();
48         }
49     }
50 }
51 } // namespace
52 
DidExceedMaxLines() const53 bool Shaper::DidExceedMaxLines() const
54 {
55     return didExceedMaxLines_;
56 }
57 
GetMinIntrinsicWidth() const58 double Shaper::GetMinIntrinsicWidth() const
59 {
60     return minIntrinsicWidth_;
61 }
62 
GetMaxIntrinsicWidth() const63 double Shaper::GetMaxIntrinsicWidth() const
64 {
65     return maxIntrinsicWidth_;
66 }
67 
SetIndents(const std::vector<float> & indents)68 void Shaper::SetIndents(const std::vector<float> &indents)
69 {
70     indents_ = indents;
71 }
72 
CreateEllipsisSpan(const TypographyStyle & ys,const TextStyle & textStyle,const std::shared_ptr<FontProviders> & fontProviders)73 std::vector<LineMetrics> Shaper::CreateEllipsisSpan(const TypographyStyle &ys, const TextStyle &textStyle,
74     const std::shared_ptr<FontProviders> &fontProviders)
75 {
76     if (ys.ellipsis.empty()) {
77         return {};
78     }
79 
80     std::vector<VariantSpan> spans = {TextSpan::MakeFromText(ys.ellipsis)};
81     for (auto &span : spans) {
82         span.SetTextStyle(textStyle);
83     }
84     auto ys2 = ys;
85     ys2.wordBreakType = WordBreakType::BREAK_ALL;
86     ys2.breakStrategy = BreakStrategy::GREEDY;
87     return DoShapeBeforeEllipsis(spans, ys2, fontProviders, MAXWIDTH);
88 }
89 
SetEllipsisProperty(std::vector<VariantSpan> & ellipsisSpans,std::vector<LineMetrics> & ellipsisMertics,double & ellipsisWidth)90 void Shaper::SetEllipsisProperty(std::vector<VariantSpan> &ellipsisSpans,
91     std::vector<LineMetrics> &ellipsisMertics, double &ellipsisWidth)
92 {
93     for (auto &metric : ellipsisMertics) {
94         for (auto &es : metric.lineSpans) {
95             ellipsisWidth += es.GetWidth();
96             ellipsisSpans.push_back(es);
97         }
98     }
99 }
100 
ConsiderEllipsis(const TypographyStyle & tstyle,const std::shared_ptr<FontProviders> & fontProviders,const double widthLimit)101 void Shaper::ConsiderEllipsis(const TypographyStyle &tstyle,
102     const std::shared_ptr<FontProviders> &fontProviders, const double widthLimit)
103 {
104     didExceedMaxLines_ = false;
105     auto maxLines = tstyle.maxLines;
106     if (maxLines < 0) {
107         maxLines = 1;
108     }
109     if (maxLines == 0) {
110         lineMetrics_.erase(lineMetrics_.begin(), lineMetrics_.end());
111         return;
112     }
113 
114     if (lineMetrics_.size() <= maxLines) {
115         if (tstyle.ellipsis.length() && tstyle.maxLines == std::numeric_limits<size_t>::max() &&
116             lineMetrics_.size() > 1) {
117             maxLines = TEXTOVERFLOWER;
118         } else {
119             return;
120         }
121     }
122 
123     const auto &textStyle = lineMetrics_.back().lineSpans.back().GetTextStyle();
124     std::vector<LineMetrics> ellipsisMertics = CreateEllipsisSpan(tstyle, textStyle, fontProviders);
125     double ellipsisWidth = 0.0;
126     std::vector<VariantSpan> ellipsisSpans;
127     SetEllipsisProperty(ellipsisSpans, ellipsisMertics, ellipsisWidth);
128 
129     EllipsisParams params{ellipsisSpans, ellipsisWidth, maxLines, widthLimit};
130     if (maxLines == 1) { // single line
131         switch (tstyle.ellipsisModal) {
132             case EllipsisModal::HEAD:
133                 params.widthLimit -= lineMetrics_.front().indent;
134                 ConsiderHeadEllipsis(tstyle, fontProviders, params);
135                 break;
136             case EllipsisModal::MIDDLE:
137                 params.widthLimit -= lineMetrics_.front().indent;
138                 ConsiderMiddleEllipsis(tstyle, fontProviders, params);
139                 break;
140             case EllipsisModal::TAIL:
141             default:
142                 ConsiderTailEllipsis(tstyle, fontProviders, params);
143                 break;
144         }
145     } else if (maxLines > 1) { // multi line
146         if (tstyle.ellipsisModal == EllipsisModal::TAIL) {
147             ConsiderTailEllipsis(tstyle, fontProviders, params);
148         } else if (maxLines < lineMetrics_.size()) {
149             lineMetrics_.erase(lineMetrics_.begin() + maxLines, lineMetrics_.end());
150         }
151     }
152     didExceedMaxLines_ = true;
153 }
154 
ComputeIntrinsicWidth(const size_t maxLines)155 void Shaper::ComputeIntrinsicWidth(const size_t maxLines)
156 {
157     maxIntrinsicWidth_ = 0.0;
158     minIntrinsicWidth_ = 0.0;
159     double lastInvisibleWidth = 0;
160     for (const auto &line : lineMetrics_) {
161         for (const auto &span : line.lineSpans) {
162             if (span == nullptr) {
163                 continue;
164             }
165 
166             auto width = span.GetWidth();
167             auto visibleWidth = span.GetVisibleWidth();
168             maxIntrinsicWidth_ += width;
169             minIntrinsicWidth_ = std::max(visibleWidth, minIntrinsicWidth_);
170             lastInvisibleWidth = width - visibleWidth;
171         }
172     }
173 
174     maxIntrinsicWidth_ -= lastInvisibleWidth;
175     if (maxLines > 1) {
176         minIntrinsicWidth_ = std::min(maxIntrinsicWidth_, minIntrinsicWidth_);
177     } else {
178         minIntrinsicWidth_ = maxIntrinsicWidth_;
179     }
180 }
181 
DoShapeBeforeEllipsis(std::vector<VariantSpan> spans,const TypographyStyle & tstyle,const std::shared_ptr<FontProviders> & fontProviders,const double widthLimit)182 std::vector<LineMetrics> Shaper::DoShapeBeforeEllipsis(std::vector<VariantSpan> spans, const TypographyStyle &tstyle,
183     const std::shared_ptr<FontProviders> &fontProviders, const double widthLimit)
184 {
185     TextBreaker tb;
186     tb.SetWidthLimit(widthLimit);
187     tb.SetIndents(indents_);
188     auto ret = tb.WordBreak(spans, tstyle, fontProviders);
189     if (ret) {
190         LOGEX_FUNC_LINE(ERROR) << "word break failed";
191         return {};
192     }
193 
194     BidiProcesser bp;
195     auto newSpans = bp.ProcessBidiText(spans, tstyle.direction);
196     if (newSpans.empty()) {
197         LOGEX_FUNC_LINE_DEBUG() << "Process BidiText failed";
198         return {};
199     }
200 
201     LineBreaker lb;
202     return lb.BreakLines(newSpans, tstyle, widthLimit, indents_);
203 }
204 
DoShape(const std::vector<VariantSpan> spans,const TypographyStyle & tstyle,const std::shared_ptr<FontProviders> & fontProviders,const double widthLimit)205 std::vector<LineMetrics> Shaper::DoShape(const std::vector<VariantSpan> spans, const TypographyStyle &tstyle,
206     const std::shared_ptr<FontProviders> &fontProviders, const double widthLimit)
207 {
208 #ifdef LOGGER_ENABLE_SCOPE
209     ScopedTrace scope("Shaper::DoShape");
210 #endif
211 
212     lineMetrics_= DoShapeBeforeEllipsis(spans, tstyle, fontProviders, widthLimit);
213     ComputeIntrinsicWidth(tstyle.maxLines);
214     ConsiderEllipsis(tstyle, fontProviders, widthLimit);
215 
216     TextMerger tm;
217     for (auto &metric : lineMetrics_) {
218         auto res = tm.MergeSpans(metric.lineSpans);
219         std::swap(res, metric.lineSpans);
220     }
221 
222     TextReverser tr;
223     for (auto &metric : lineMetrics_) {
224         tr.ReverseRTLText(metric.lineSpans);
225         tr.ProcessTypoDirection(metric.lineSpans, tstyle.direction);
226     }
227 
228     TextShaper textShaper;
229     for (const auto &metric : lineMetrics_) {
230         for (const auto &span : metric.lineSpans) {
231             textShaper.Shape(span, tstyle, fontProviders);
232         }
233     }
234     DumpLineMetrics(lineMetrics_);
235     return lineMetrics_;
236 }
237 
ConsiderHeadEllipsis(const TypographyStyle & ys,const std::shared_ptr<FontProviders> & fontProviders,EllipsisParams params)238 void Shaper::ConsiderHeadEllipsis(const TypographyStyle &ys, const std::shared_ptr<FontProviders> &fontProviders,
239     EllipsisParams params)
240 {
241     bool isErase = false;
242     auto &lastLine = lineMetrics_.back();
243     double lastLineWidth = lastLine.GetAllSpanWidth();
244     if (params.maxLines < lineMetrics_.size()) {
245         if (params.ellipsisWidth > 0) {
246             // lineMetrics_.size() - 2 is the index of second to last
247             while (!lineMetrics_[lineMetrics_.size() - 2].lineSpans.empty() &&
248                 lastLineWidth + params.ellipsisWidth < params.widthLimit) {
249                 // lineMetrics_.size() - 2 is the index of second to last
250                 auto lastSpan = lineMetrics_[lineMetrics_.size() - 2].lineSpans.back();
251                 // lineMetrics_.size() - 2 is the index of second to last
252                 lineMetrics_[lineMetrics_.size() - 2].lineSpans.pop_back();
253                 lastLine.lineSpans.insert(lastLine.lineSpans.begin(), lastSpan);
254                 lastLineWidth = lastLine.GetAllSpanWidth();
255             }
256             lineMetrics_.erase(lineMetrics_.begin(), lineMetrics_.end() - params.maxLines);
257             isErase = true;
258         } else {
259             lineMetrics_.erase(lineMetrics_.begin() + params.maxLines, lineMetrics_.end());
260         }
261     }
262 
263     if (params.ellipsisSpans.empty() || (!isErase && lastLineWidth <= params.widthLimit)) {
264         return;
265     }
266 
267     std::vector<VariantSpan> &lastLineSpans = lineMetrics_.back().lineSpans;
268     auto ts = lastLineSpans.front().TryToTextSpan();
269     if (ts == nullptr) {
270         if (lastLineWidth + params.ellipsisWidth > params.widthLimit) {
271             lastLineSpans.erase(lastLineSpans.begin());
272             lastLineSpans.insert(lastLineSpans.begin(), params.ellipsisSpans.begin(), params.ellipsisSpans.end());
273         } else if (isErase) {
274             lastLineSpans.insert(lastLineSpans.begin(), params.ellipsisSpans.begin(), params.ellipsisSpans.end());
275         }
276     } else {
277         if (lastLineWidth + params.ellipsisWidth > params.widthLimit) {
278             double exceedWidth = lastLineWidth + params.ellipsisWidth - params.widthLimit;
279             auto firstSpan = lastLineSpans.front();
280             lastLineSpans.erase(lastLineSpans.begin());
281             std::vector<LineMetrics> partlySpan = CreatePartlySpan(false, ys, fontProviders, firstSpan, exceedWidth);
282             if (!partlySpan.empty()) {
283                 std::vector<VariantSpan> partlyLineSpans = partlySpan.front().lineSpans;
284                 lastLineSpans.insert(lastLineSpans.begin(), partlyLineSpans.begin(), partlyLineSpans.end());
285             }
286             lastLineSpans.insert(lastLineSpans.begin(), params.ellipsisSpans.begin(), params.ellipsisSpans.end());
287         } else if (isErase) {
288             lastLineSpans.insert(lastLineSpans.begin(), params.ellipsisSpans.begin(), params.ellipsisSpans.end());
289         }
290     }
291 }
292 
ConsiderLastLine(const TypographyStyle & style,const std::shared_ptr<FontProviders> & fontProviders,EllipsisParams params,const bool isErase)293 void Shaper::ConsiderLastLine(const TypographyStyle &style, const std::shared_ptr<FontProviders> &fontProviders,
294     EllipsisParams params, const bool isErase)
295 {
296     // maxLines - 1 is the index of last LineMetrics
297     auto &lastLine = params.maxLines < lineMetrics_.size() ? lineMetrics_[params.maxLines - 1] : lineMetrics_.back();
298     double lastLineWidth = lastLine.GetAllSpanWidth();
299     bool isExceed = (static_cast<int>(lastLineWidth + params.ellipsisWidth) > static_cast<int>(params.widthLimit));
300     if (isExceed) {
301         while ((!lastLine.lineSpans.empty()) && isExceed) {
302             auto lastSpan = lastLine.lineSpans.back();
303             lastLine.lineSpans.pop_back();
304             double exceedWidth = lastLineWidth + params.ellipsisWidth - params.widthLimit;
305             std::vector<LineMetrics> partlySpan = CreatePartlySpan(true, style, fontProviders, lastSpan, exceedWidth);
306             if (!partlySpan.empty()) {
307                 std::vector<VariantSpan> partlyLineSpans = partlySpan.front().lineSpans;
308                 lastLine.lineSpans.insert(lastLine.lineSpans.end(), partlyLineSpans.begin(),
309                     partlyLineSpans.end());
310             }
311             lastLineWidth = lastLine.GetAllSpanWidth();
312             isExceed = (static_cast<int>(lastLineWidth + params.ellipsisWidth) > static_cast<int>(params.widthLimit));
313         }
314         lastLine.lineSpans.insert(lastLine.lineSpans.end(), params.ellipsisSpans.begin(),
315             params.ellipsisSpans.end());
316     } else if (isErase) {
317         lastLine.lineSpans.insert(lastLine.lineSpans.end(), params.ellipsisSpans.begin(),
318             params.ellipsisSpans.end());
319     }
320 }
321 
ConsiderTailEllipsis(const TypographyStyle & style,const std::shared_ptr<FontProviders> & fontProviders,EllipsisParams params)322 void Shaper::ConsiderTailEllipsis(const TypographyStyle &style, const std::shared_ptr<FontProviders> &fontProviders,
323     EllipsisParams params)
324 {
325     bool isErase = false;
326     // maxLines - 1 is the index of last LineMetrics
327     auto &lastLine = params.maxLines < lineMetrics_.size() ? lineMetrics_[params.maxLines - 1] : lineMetrics_.back();
328     double lastLineWidth = lastLine.GetAllSpanWidth();
329     params.widthLimit -= lastLine.indent;
330     if (params.maxLines < lineMetrics_.size()) {
331         if (params.ellipsisWidth > 0 && lastLineWidth + params.ellipsisWidth < params.widthLimit &&
332             style.wordBreakType == WordBreakType::BREAK_ALL) {
333             lastLine.lineSpans.push_back(lineMetrics_[params.maxLines].lineSpans.front());
334             lastLineWidth = lastLine.GetAllSpanWidth();
335         }
336         lineMetrics_.erase(lineMetrics_.begin() + params.maxLines, lineMetrics_.end());
337         isErase = true;
338     }
339 
340     if (params.ellipsisSpans.empty() || (!isErase && lastLineWidth <= params.widthLimit)) {
341         return;
342     }
343 
344     auto ts = lastLine.lineSpans.back().TryToTextSpan();
345     if (ts == nullptr) {
346         if (lastLineWidth + params.ellipsisWidth > params.widthLimit) {
347             lastLine.lineSpans.pop_back();
348             lastLine.lineSpans.insert(lastLine.lineSpans.end(), params.ellipsisSpans.begin(),
349                 params.ellipsisSpans.end());
350         } else if (isErase) {
351             lastLine.lineSpans.insert(lastLine.lineSpans.end(), params.ellipsisSpans.begin(),
352                 params.ellipsisSpans.end());
353         }
354     } else {
355         ConsiderLastLine(style, fontProviders, params, isErase);
356     }
357 }
358 
CreatePartlySpan(const bool cutRight,const TypographyStyle & ys,const std::shared_ptr<FontProviders> & fontProviders,const VariantSpan & span,const double exceedWidth)359 std::vector<LineMetrics> Shaper::CreatePartlySpan(const bool cutRight, const TypographyStyle &ys,
360     const std::shared_ptr<FontProviders> &fontProviders, const VariantSpan &span, const double exceedWidth)
361 {
362     auto textSpan = span.TryToTextSpan();
363     if (textSpan == nullptr) {
364         return {};
365     }
366 
367     size_t startIndex = static_cast<size_t>(textSpan->cgs_.GetRange().start);
368     size_t endIndex = static_cast<size_t>(textSpan->cgs_.GetRange().end);
369     double deletedWidth = 0.0;
370     while (startIndex < endIndex) {
371         if (deletedWidth >= exceedWidth) {
372             break;
373         }
374         if (cutRight) {
375             endIndex--;
376             deletedWidth += textSpan->cgs_.GetCharWidth(endIndex);
377         } else {
378             deletedWidth += textSpan->cgs_.GetCharWidth(startIndex);
379             startIndex++;
380         }
381     }
382 
383     if (startIndex < endIndex) {
384         // endIndex - 1 is the index of end
385         std::vector<uint16_t> chars = textSpan->cgs_.GetCharsToU16(startIndex, endIndex - 1, SpacesModel::NORMAL);
386         VariantSpan partlySpan(TextSpan::MakeFromText(chars));
387         partlySpan.SetTextStyle(span.GetTextStyle());
388         std::vector<VariantSpan> spans = {partlySpan};
389         return DoShapeBeforeEllipsis(spans, ys, fontProviders, MAXWIDTH);
390     } else {
391         return {};
392     }
393 }
394 
CalcCharsIndex(const std::shared_ptr<TextSpan> textSpan,size_t & leftIndex,size_t & rightIndex,size_t & maxIndex,const int avalibleWidth) const395 bool Shaper::CalcCharsIndex(const std::shared_ptr<TextSpan> textSpan, size_t &leftIndex,
396     size_t &rightIndex, size_t &maxIndex, const int avalibleWidth) const
397 {
398     if (textSpan == nullptr) {
399         return false;
400     }
401     leftIndex = 0;
402     maxIndex = textSpan->cgs_.GetSize();
403     rightIndex = maxIndex;
404 
405     double leftCharsWidth = 0.0;
406     double rightCharsWidth = 0.0;
407     bool isLeft = true;
408     bool isNormal = true;
409     int charsWidth = static_cast<int>(leftCharsWidth + rightCharsWidth);
410     while (charsWidth <= avalibleWidth) {
411         if (isLeft) {
412             leftCharsWidth += textSpan->cgs_.GetCharWidth(leftIndex);
413             charsWidth = static_cast<int>(leftCharsWidth + rightCharsWidth);
414             if (charsWidth > avalibleWidth) {
415                 leftIndex--;
416                 break;
417             }
418             isLeft = false;
419         } else {
420             rightIndex--;
421             rightCharsWidth += textSpan->cgs_.GetCharWidth(rightIndex);
422             charsWidth = static_cast<int>(leftCharsWidth + rightCharsWidth);
423             if (charsWidth > avalibleWidth) {
424                 rightIndex++;
425                 break;
426             }
427             leftIndex++;
428             isLeft = true;
429         }
430         if (leftIndex > rightIndex) {
431             isNormal = false;
432             break;
433         }
434     }
435     return isNormal;
436 }
437 
SplitJointLeftSpans(const EllipsisParams & params,const size_t leftIndex,const TypographyStyle & style,const std::shared_ptr<FontProviders> & fontProviders,const VariantSpan & span)438 void Shaper::SplitJointLeftSpans(const EllipsisParams &params, const size_t leftIndex,
439     const TypographyStyle &style, const std::shared_ptr<FontProviders> &fontProviders, const VariantSpan &span)
440 {
441     std::shared_ptr<TextSpan> textSpan = span.TryToTextSpan();
442     if (textSpan == nullptr) {
443         return;
444     }
445 
446     // 0 means the first index of the array,true means handles spaces at the end of the left index of the array
447     std::vector<uint16_t> leftGroups = textSpan->cgs_.GetCharsToU16(0, leftIndex, SpacesModel::NORMAL);
448     VariantSpan leftSpan(TextSpan::MakeFromText(leftGroups));
449     leftSpan.SetTextStyle(span.GetTextStyle());
450     std::vector<VariantSpan> leftVariantSpans = {leftSpan};
451     lineMetrics_ = DoShapeBeforeEllipsis(leftVariantSpans, style, fontProviders, params.widthLimit);
452     lineMetrics_.back().lineSpans.insert(lineMetrics_.back().lineSpans.end(),
453         params.ellipsisSpans.begin(), params.ellipsisSpans.end());
454 }
455 
SplitJointRightSpans(const EllipsisParams & params,const size_t rightIndex,const TypographyStyle & style,const std::shared_ptr<FontProviders> & fontProviders,const VariantSpan & span)456 void Shaper::SplitJointRightSpans(const EllipsisParams &params, const size_t rightIndex,
457     const TypographyStyle &style, const std::shared_ptr<FontProviders> &fontProviders, const VariantSpan &span)
458 {
459     std::shared_ptr<TextSpan> textSpan = span.TryToTextSpan();
460     if (textSpan == nullptr) {
461         return;
462     }
463     size_t maxIndex = textSpan->cgs_.GetSize();
464     // maxIndex - 1 means last index of the array,false means handles spaces at the end of the right index of the array
465     std::vector<uint16_t> rightGroups = textSpan->cgs_.GetCharsToU16(rightIndex, maxIndex - 1, SpacesModel::NORMAL);
466     VariantSpan rightSpan(TextSpan::MakeFromText(rightGroups));
467     rightSpan.SetTextStyle(span.GetTextStyle());
468     std::vector<VariantSpan> rightVariantSpans = {rightSpan};
469     if (style.ellipsisModal == EllipsisModal::HEAD) {
470         lineMetrics_ = DoShapeBeforeEllipsis(rightVariantSpans, style, fontProviders, params.widthLimit);
471         lineMetrics_.front().lineSpans.insert(lineMetrics_.front().lineSpans.begin(),
472             params.ellipsisSpans.begin(), params.ellipsisSpans.end());
473     } else if (style.ellipsisModal == EllipsisModal::MIDDLE) {
474         std::vector<LineMetrics> rightLineMetrics = DoShapeBeforeEllipsis(rightVariantSpans,
475             style, fontProviders, params.widthLimit);
476         lineMetrics_.back().lineSpans.insert(lineMetrics_.back().lineSpans.end(),
477             rightLineMetrics.front().lineSpans.begin(), rightLineMetrics.front().lineSpans.end());
478     }
479 }
480 
CalcMidSpanIndex(const std::vector<VariantSpan> & spans,size_t & leftIndex,size_t & rightIndex,struct SpansWidth & spansWidth,const int avalibleWidth)481 bool Shaper::CalcMidSpanIndex(const std::vector<VariantSpan> &spans, size_t &leftIndex, size_t &rightIndex,
482     struct SpansWidth &spansWidth, const int avalibleWidth)
483 {
484     double leftSpansWidth = 0.0;
485     double rightSpansWidth = 0.0;
486     bool isLeft = true;
487     bool isNormal = true;
488     int curSpansWidth = static_cast<int>(leftSpansWidth + rightSpansWidth);
489     while (curSpansWidth <= avalibleWidth) {
490         if (isLeft) {
491             leftSpansWidth += spans.at(leftIndex).GetWidth();
492             curSpansWidth = static_cast<int>(leftSpansWidth + rightSpansWidth);
493             if (curSpansWidth > avalibleWidth) {
494                 break;
495             }
496             isLeft = false;
497         } else {
498             rightIndex--;
499             rightSpansWidth += spans.at(rightIndex).GetWidth();
500             curSpansWidth = static_cast<int>(leftSpansWidth + rightSpansWidth);
501             if (curSpansWidth > avalibleWidth) {
502                 break;
503             }
504             leftIndex++;
505             isLeft = true;
506         }
507         if (leftIndex >= rightIndex) {
508             isNormal = false;
509             break;
510         }
511     }
512     spansWidth.leftWidth = leftSpansWidth;
513     spansWidth.rightWidth = rightSpansWidth;
514     return isNormal;
515 }
516 
ConsideMidSpanEllipsis(const TypographyStyle & style,const std::shared_ptr<FontProviders> & fontProviders,const EllipsisParams & params,const std::vector<VariantSpan> & spans)517 void Shaper::ConsideMidSpanEllipsis(const TypographyStyle &style, const std::shared_ptr<FontProviders> &fontProviders,
518     const EllipsisParams &params, const std::vector<VariantSpan> &spans)
519 {
520     struct SpansWidth spansWidth;
521     size_t leftIndex = 0;
522     size_t rightIndex = spans.size();
523     int avalibleWidth = static_cast<int>(params.widthLimit - params.ellipsisWidth);
524     auto &firstLineSpans = lineMetrics_.front().lineSpans;
525     if (!CalcMidSpanIndex(spans, leftIndex, rightIndex, spansWidth, avalibleWidth)) {
526         firstLineSpans = params.ellipsisSpans;
527         lineMetrics_.erase(lineMetrics_.begin() + params.maxLines, lineMetrics_.end());
528         return;
529     }
530     std::shared_ptr<TextSpan> textSpan = spans.front().TryToTextSpan();
531     double exceedWidth = spansWidth.leftWidth + spansWidth.rightWidth + params.ellipsisWidth - params.widthLimit;
532     std::vector<LineMetrics> partlySpan;
533     bool isLeft = static_cast<int>(spansWidth.leftWidth) > static_cast<int>(spansWidth.rightWidth);
534     if (isLeft) {
535         if (leftIndex >= spans.size()) {
536             firstLineSpans = params.ellipsisSpans;
537             lineMetrics_.erase(lineMetrics_.begin() + params.maxLines, lineMetrics_.end());
538             return;
539         }
540         partlySpan = CreatePartlySpan(true, style, fontProviders, spans.at(leftIndex), exceedWidth);
541         leftIndex--;
542     } else {
543         if (rightIndex < spans.size()) {
544             partlySpan = CreatePartlySpan(false, style, fontProviders, spans.at(rightIndex), exceedWidth);
545             rightIndex++;
546         }
547     }
548 
549     firstLineSpans = params.ellipsisSpans;
550     lineMetrics_.erase(lineMetrics_.begin() + params.maxLines, lineMetrics_.end());
551 
552     if (leftIndex < spans.size()) {
553         firstLineSpans.insert(firstLineSpans.begin(), spans.begin(), spans.begin() + leftIndex + 1);
554     }
555     if (isLeft) {
556         if (!partlySpan.empty()) {
557             std::vector<VariantSpan> partlyLineSpans = partlySpan.front().lineSpans;
558             firstLineSpans.insert(firstLineSpans.end() - params.ellipsisSpans.size(),
559                 partlyLineSpans.begin(), partlyLineSpans.end());
560         }
561     } else {
562         if (!partlySpan.empty()) {
563             std::vector<VariantSpan> partlyLineSpans = partlySpan.front().lineSpans;
564             firstLineSpans.insert(firstLineSpans.end(), partlyLineSpans.begin(), partlyLineSpans.end());
565         }
566     }
567     if (rightIndex < spans.size()) {
568         firstLineSpans.insert(firstLineSpans.end(), spans.begin() + rightIndex, spans.end());
569     }
570 }
571 
GetLoopNum(VariantSpan & span,std::vector<VariantSpan> & spans,int & loopNum,int & breakPos,bool & haveAnySpan)572 void Shaper::GetLoopNum(VariantSpan &span, std::vector<VariantSpan> &spans,
573     int &loopNum, int &breakPos, bool &haveAnySpan)
574 {
575     auto textSpan = span.TryToTextSpan();
576     if (textSpan && textSpan->cgs_.GetSize()) {
577         breakPos = textSpan->cgs_.FindHardBreakPos();
578         if (breakPos == -1) {
579             if (static_cast<int>(textSpan->cgs_.GetSize()) == textSpan->cgs_.GetRange().end) {
580                 loopNum += textSpan->cgs_.GetRange().end;
581             }
582             spans.push_back(span);
583         } else {
584             loopNum += breakPos;
585             spans.push_back(span);
586         }
587     } else {
588         haveAnySpan = true;
589     }
590 }
591 
GetAllTextSpan(std::vector<VariantSpan> & spans,int & loopNum,int & breakPos)592 void Shaper::GetAllTextSpan(std::vector<VariantSpan> &spans, int &loopNum, int &breakPos)
593 {
594     bool haveAnySpan = false;
595     for (auto metric : lineMetrics_) {
596         for (auto span : metric.lineSpans) {
597             GetLoopNum(span, spans, loopNum, breakPos, haveAnySpan);
598             if (haveAnySpan || (breakPos != -1)) {
599                 break;
600             }
601         }
602         if (haveAnySpan || (breakPos != -1)) {
603             break;
604         }
605     }
606 }
607 
HaveExceedWidth(const std::vector<VariantSpan> & spans,struct SpanPosition & spanPos,const int & charsWidth,struct SpansWidth & spanWidth)608 bool Shaper::HaveExceedWidth(const std::vector<VariantSpan> &spans, struct SpanPosition &spanPos,
609     const int &charsWidth, struct SpansWidth &spanWidth)
610 {
611     if (charsWidth > spanPos.avalibleWidth) {
612         spanPos.rightCharIndex++;
613         if (spanPos.rightCharIndex == spans.at(spanPos.rightSpanIndex).TryToTextSpan()->cgs_.GetRange().end) {
614             spanPos.rightSpanIndex++;
615             if (spanPos.rightSpanIndex <= spanPos.maxSpanIndex) {
616                 spanPos.rightCharIndex = spans.at(spanPos.rightSpanIndex).TryToTextSpan()->cgs_.GetRange().start;
617             }
618         }
619         return true;
620     }
621     return false;
622 }
623 
CalcAvalibleWidth(const std::vector<VariantSpan> & spans,struct SpanPosition & spanPos,bool & isLeft,int & charsWidth,struct SpansWidth & spanWidth)624 bool Shaper::CalcAvalibleWidth(const std::vector<VariantSpan> &spans, struct SpanPosition &spanPos,
625     bool &isLeft, int &charsWidth, struct SpansWidth &spanWidth)
626 {
627     if (isLeft) {
628         auto leftSpanCharGroups = spans.at(spanPos.leftSpanIndex).TryToTextSpan()->cgs_;
629         spanWidth.leftWidth += leftSpanCharGroups.GetCharWidth(spanPos.leftCharIndex);
630         charsWidth = static_cast<int>(spanWidth.leftWidth + spanWidth.rightWidth);
631         if (charsWidth > spanPos.avalibleWidth) {
632             if (spanPos.leftSpanIndex &&
633                 spanPos.leftCharIndex == spans.at(spanPos.leftSpanIndex).TryToTextSpan()->cgs_.GetRange().start) {
634                 spanPos.leftSpanIndex--;
635                 spanPos.leftCharIndex = spans.at(spanPos.leftSpanIndex).TryToTextSpan()->cgs_.GetRange().end;
636             }
637             spanPos.leftCharIndex--;
638             return true;
639         }
640 
641         if (spanPos.rightCharIndex == spans.at(spanPos.rightSpanIndex).TryToTextSpan()->cgs_.GetRange().start) {
642             spanPos.rightSpanIndex--;
643             spanPos.rightCharIndex = spans.at(spanPos.rightSpanIndex).TryToTextSpan()->cgs_.GetRange().end;
644         }
645         spanPos.rightCharIndex--;
646         isLeft = false;
647     } else {
648         auto rightSpanCharGroups = spans.at(spanPos.rightSpanIndex).TryToTextSpan()->cgs_;
649         spanWidth.rightWidth += rightSpanCharGroups.GetCharWidth(spanPos.rightCharIndex);
650         charsWidth = static_cast<int>(spanWidth.leftWidth + spanWidth.rightWidth);
651 
652         if (HaveExceedWidth(spans, spanPos, charsWidth, spanWidth)) {
653             return true;
654         }
655 
656         spanPos.leftCharIndex++;
657         if (spanPos.leftCharIndex == spans.at(spanPos.leftSpanIndex).TryToTextSpan()->cgs_.GetRange().end) {
658             spanPos.leftSpanIndex++;
659             if (spanPos.leftSpanIndex <= spanPos.rightSpanIndex) {
660                 spanPos.leftCharIndex = spans.at(spanPos.leftSpanIndex).TryToTextSpan()->cgs_.GetRange().start;
661             }
662         }
663         isLeft = true;
664     }
665     return false;
666 }
667 
CalcSpanPosition(const std::vector<VariantSpan> & spans,struct SpanPosition & spanPos,const int & breakPos,const int loopNum)668 bool Shaper::CalcSpanPosition(const std::vector<VariantSpan> &spans, struct SpanPosition &spanPos,
669     const int &breakPos, const int loopNum)
670 {
671     spanPos.leftSpanIndex = 0;
672     spanPos.rightSpanIndex = spans.size() - 1;
673     spanPos.maxSpanIndex = spanPos.rightSpanIndex;
674     spanPos.leftCharIndex = spans.at(spanPos.leftSpanIndex).TryToTextSpan()->cgs_.GetRange().start;
675     spanPos.rightCharIndex = spans.at(spanPos.rightSpanIndex).TryToTextSpan()->cgs_.GetRange().end;
676 
677     if (breakPos != -1) {
678         spanPos.rightCharIndex = breakPos;
679         if (spanPos.maxSpanIndex &&
680             (spanPos.rightCharIndex == spans.at(spanPos.maxSpanIndex).TryToTextSpan()->cgs_.GetRange().start)) {
681             spanPos.maxSpanIndex--;
682             spanPos.rightSpanIndex = spanPos.maxSpanIndex;
683             spanPos.rightCharIndex = spans.at(spanPos.maxSpanIndex).TryToTextSpan()->cgs_.GetRange().end;
684         }
685     }
686 
687     struct SpansWidth spanWidth;
688     spanWidth.leftWidth = 0.0;
689     spanWidth.rightWidth = 0.0;
690 
691     bool isLeft = true;
692     bool isNormal = true;
693     int charsWidth = 0;
694     for (auto i = 0; i < loopNum; i++) {
695         if (charsWidth <= spanPos.avalibleWidth) {
696             if (CalcAvalibleWidth(spans, spanPos, isLeft, charsWidth, spanWidth)) {
697                 break;
698             }
699             if (spanPos.leftSpanIndex == spanPos.rightSpanIndex && spanPos.leftCharIndex == spanPos.rightCharIndex) {
700                 break;
701             }
702             if (spanPos.leftSpanIndex > spanPos.rightSpanIndex) {
703                 isNormal = false;
704                 break;
705             }
706         }
707     }
708     return isNormal;
709 }
710 
JointCriticalLeftSpans(const std::vector<VariantSpan> & spans,std::vector<VariantSpan> & leftLineSpans,struct SpanPosition & spanPos,const TypographyStyle & style,const std::shared_ptr<FontProviders> & fontProviders)711 void Shaper::JointCriticalLeftSpans(const std::vector<VariantSpan> &spans, std::vector<VariantSpan> &leftLineSpans,
712     struct SpanPosition &spanPos, const TypographyStyle &style, const std::shared_ptr<FontProviders> &fontProviders)
713 {
714     auto leftCharGroupsRange = spans.at(spanPos.leftSpanIndex).TryToTextSpan()->cgs_.GetRange();
715     auto leftEnd = leftCharGroupsRange.end;
716     auto leftStart = leftCharGroupsRange.start;
717     leftEnd--;
718     if (spanPos.leftCharIndex == leftEnd) {
719         leftLineSpans.insert(leftLineSpans.end(), spans.at(spanPos.leftSpanIndex));
720     } else if ((spanPos.leftCharIndex >= leftStart) && (spanPos.leftCharIndex < leftEnd)) {
721         std::vector<uint16_t> subChars =
722             spans.at(spanPos.leftSpanIndex).TryToTextSpan()->cgs_.GetSubCharsToU16(leftStart, spanPos.leftCharIndex);
723         if (subChars.empty()) {
724             return;
725         }
726         VariantSpan leftSpan(TextSpan::MakeFromText(subChars));
727         leftSpan.SetTextStyle(spans.at(spanPos.leftSpanIndex).GetTextStyle());
728         std::vector<VariantSpan> newSpans = {leftSpan};
729         std::vector<LineMetrics> partlySpan = DoShapeBeforeEllipsis(newSpans, style, fontProviders, MAXWIDTH);
730         if (!partlySpan.empty()) {
731             std::vector<VariantSpan> partlyLineSpans = partlySpan.front().lineSpans;
732             leftLineSpans.insert(leftLineSpans.end(), partlyLineSpans.begin(), partlyLineSpans.end());
733         }
734     }
735 }
736 
SplitJointLeftLineSpans(const std::vector<VariantSpan> & spans,std::vector<VariantSpan> & leftLineSpans,struct SpanPosition & spanPos,const TypographyStyle & style,const std::shared_ptr<FontProviders> & fontProviders)737 void Shaper::SplitJointLeftLineSpans(const std::vector<VariantSpan> &spans, std::vector<VariantSpan> &leftLineSpans,
738     struct SpanPosition &spanPos, const TypographyStyle &style, const std::shared_ptr<FontProviders> &fontProviders)
739 {
740     for (size_t index = 0; index <= spanPos.leftSpanIndex; index++) {
741         if (index == spanPos.leftSpanIndex) {
742             JointCriticalLeftSpans(spans, leftLineSpans, spanPos, style, fontProviders);
743         } else {
744             leftLineSpans.insert(leftLineSpans.end(), spans.at(index));
745         }
746     }
747 }
748 
JointRightLineSpans(const std::vector<VariantSpan> & spans,std::vector<VariantSpan> & rightLineSpans,struct SpanPosition & spanPos,const TypographyStyle & style,const std::shared_ptr<FontProviders> & fontProviders)749 void Shaper::JointRightLineSpans(const std::vector<VariantSpan> &spans, std::vector<VariantSpan> &rightLineSpans,
750     struct SpanPosition &spanPos, const TypographyStyle &style, const std::shared_ptr<FontProviders> &fontProviders)
751 {
752     const auto &rightCharGroupsRange = spans.at(spanPos.curIndex).TryToTextSpan()->cgs_.GetRange();
753     auto rightEnd = rightCharGroupsRange.end;
754     rightEnd--;
755 
756     if ((spanPos.curIndex > spanPos.rightSpanIndex && spanPos.curIndex <= spanPos.maxSpanIndex) ||
757         (spanPos.curIndex != spanPos.leftSpanIndex && spanPos.curIndex == spanPos.maxSpanIndex &&
758         spanPos.rightCharIndex == rightCharGroupsRange.start)) {
759         rightLineSpans.insert(rightLineSpans.end(), spans.at(spanPos.curIndex));
760     } else if ((spanPos.rightCharIndex >  rightCharGroupsRange.start) && (spanPos.rightCharIndex <= rightEnd)) {
761         std::vector<uint16_t> subChars =
762             spans.at(spanPos.curIndex).TryToTextSpan()->cgs_.GetSubCharsToU16(spanPos.rightCharIndex, rightEnd);
763         if (subChars.empty()) {
764             return;
765         }
766         VariantSpan rightSpan(TextSpan::MakeFromText(subChars));
767         rightSpan.SetTextStyle(spans.at(spanPos.curIndex).GetTextStyle());
768         std::vector<VariantSpan> newSpans = {rightSpan};
769         std::vector<LineMetrics> partlySpan = DoShapeBeforeEllipsis(newSpans, style, fontProviders, MAXWIDTH);
770         if (!partlySpan.empty()) {
771             std::vector<VariantSpan> partlyLineSpans = partlySpan.front().lineSpans;
772             rightLineSpans.insert(rightLineSpans.end(), partlyLineSpans.begin(), partlyLineSpans.end());
773         }
774     }
775 }
776 
SplitJointRightLineSpans(const std::vector<VariantSpan> & spans,std::vector<VariantSpan> & rightLineSpans,struct SpanPosition & spanPos,const TypographyStyle & style,const std::shared_ptr<FontProviders> & fontProviders)777 void Shaper::SplitJointRightLineSpans(const std::vector<VariantSpan> &spans, std::vector<VariantSpan> &rightLineSpans,
778     struct SpanPosition &spanPos, const TypographyStyle &style, const std::shared_ptr<FontProviders> &fontProviders)
779 {
780     for (size_t index = spanPos.rightSpanIndex; index <= spanPos.maxSpanIndex; index++) {
781         if (index == spanPos.leftSpanIndex || (index != spanPos.leftSpanIndex && index == spanPos.maxSpanIndex)) {
782             spanPos.curIndex = index;
783             JointRightLineSpans(spans, rightLineSpans, spanPos, style, fontProviders);
784         } else {
785             rightLineSpans.insert(rightLineSpans.end(), spans.at(index));
786         }
787     }
788 }
789 
SplitJointSpans(const std::vector<VariantSpan> & spans,const EllipsisParams & params,struct SpanPosition & spanPos,const TypographyStyle & style,const std::shared_ptr<FontProviders> & fontProviders)790 void Shaper::SplitJointSpans(const std::vector<VariantSpan> &spans, const EllipsisParams &params,
791     struct SpanPosition &spanPos, const TypographyStyle &style, const std::shared_ptr<FontProviders> &fontProviders)
792 {
793     if ((spanPos.leftSpanIndex == spanPos.rightSpanIndex) && (spanPos.leftCharIndex == spanPos.rightCharIndex)) {
794         std::vector<VariantSpan> lineSpans;
795         for (size_t i = 0; i < spans.size(); i++) {
796             lineSpans.insert(lineSpans.end(), spans.at(i));
797         }
798         auto &firstLineSpans = lineMetrics_.front().lineSpans;
799         firstLineSpans = lineSpans;
800         lineMetrics_.erase(lineMetrics_.begin() + params.maxLines, lineMetrics_.end());
801         firstLineSpans.insert(firstLineSpans.end(), params.ellipsisSpans.begin(), params.ellipsisSpans.end());
802     } else {
803         std::vector<VariantSpan> leftLineSpans;
804         SplitJointLeftLineSpans(spans, leftLineSpans, spanPos, style, fontProviders);
805 
806         auto &firstLineSpans = lineMetrics_.front().lineSpans;
807         if (leftLineSpans.empty()) {
808             firstLineSpans= params.ellipsisSpans;
809             lineMetrics_.erase(lineMetrics_.begin() + params.maxLines, lineMetrics_.end());
810             return;
811         } else {
812             firstLineSpans = leftLineSpans;
813             lineMetrics_.erase(lineMetrics_.begin() + params.maxLines, lineMetrics_.end());
814             firstLineSpans.insert(firstLineSpans.end(), params.ellipsisSpans.begin(), params.ellipsisSpans.end());
815         }
816 
817         std::vector<VariantSpan> rightLineSpans;
818         SplitJointRightLineSpans(spans, rightLineSpans, spanPos, style, fontProviders);
819         if (!rightLineSpans.empty()) {
820             firstLineSpans.insert(firstLineSpans.end(), rightLineSpans.begin(), rightLineSpans.end());
821         }
822     }
823 }
824 
ConsiderMiddleEllipsis(const TypographyStyle & style,const std::shared_ptr<FontProviders> & fontProviders,EllipsisParams params)825 void Shaper::ConsiderMiddleEllipsis(const TypographyStyle &style, const std::shared_ptr<FontProviders> &fontProviders,
826     EllipsisParams params)
827 {
828     if (params.maxLines >= lineMetrics_.size() || params.ellipsisSpans.empty()) {
829         return;
830     }
831 
832     int avalibleWidth = static_cast<int>(params.widthLimit - params.ellipsisWidth);
833     if (avalibleWidth < 0) {
834         lineMetrics_.front().lineSpans = params.ellipsisSpans;
835         lineMetrics_.erase(lineMetrics_.begin() + params.maxLines, lineMetrics_.end());
836         return;
837     }
838 
839     std::vector<VariantSpan> spans;
840     int loopNum = 0;
841     int breakPos = -1;
842     GetAllTextSpan(spans, loopNum, breakPos);
843 
844     if (spans.empty() || !loopNum) {
845         lineMetrics_.front().lineSpans = params.ellipsisSpans;
846         lineMetrics_.erase(lineMetrics_.begin() + params.maxLines, lineMetrics_.end());
847         return;
848     }
849 
850     struct SpanPosition spanPos;
851     spanPos.avalibleWidth = avalibleWidth;
852     bool isNormal = CalcSpanPosition(spans, spanPos, breakPos, loopNum);
853     std::shared_ptr<TextSpan> textSpan = nullptr;
854     if (!spanPos.leftSpanIndex) {
855         textSpan = spans.at(spanPos.leftSpanIndex).TryToTextSpan();
856     }
857 
858     if ((!isNormal) || (!spanPos.leftSpanIndex && textSpan != nullptr &&
859         spanPos.leftCharIndex < textSpan->cgs_.GetRange().start)) {
860         lineMetrics_.front().lineSpans = params.ellipsisSpans;
861         lineMetrics_.erase(lineMetrics_.begin() + params.maxLines, lineMetrics_.end());
862         return;
863     }
864 
865     SplitJointSpans(spans, params, spanPos, style, fontProviders);
866 }
867 } // namespace TextEngine
868 } // namespace Rosen
869 } // namespace OHOS
870