• 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 "line_breaker.h"
17 
18 #include "char_groups.h"
19 #include "floating.h"
20 #include "texgine/any_span.h"
21 #include "texgine_exception.h"
22 #include "texgine/utils/exlog.h"
23 #ifdef LOGGER_ENABLE_SCOPE
24 #include "texgine/utils/trace.h"
25 #endif
26 #include "text_merger.h"
27 #include "text_span.h"
28 
29 namespace OHOS {
30 namespace Rosen {
31 namespace TextEngine {
32 #define FAILED 1
33 
BreakLines(std::vector<VariantSpan> & spans,const TypographyStyle & tstyle,const double widthLimit,const std::vector<float> & indents)34 std::vector<LineMetrics> LineBreaker::BreakLines(std::vector<VariantSpan> &spans,
35     const TypographyStyle &tstyle, const double widthLimit, const std::vector<float> &indents)
36 {
37     LOGSCOPED(sl, LOGEX_FUNC_LINE_DEBUG(), "BreakLines");
38     auto ss = GenerateScoreSpans(spans);
39     DoBreakLines(ss, widthLimit, tstyle, indents);
40     auto lineBreaks = GenerateBreaks(spans, ss);
41     std::vector<LineMetrics> lineMetrics = GenerateLineMetrics(widthLimit, spans, lineBreaks, indents);
42     ProcessHardBreak(lineMetrics);
43     return lineMetrics;
44 }
45 
GenerateScoreSpans(const std::vector<VariantSpan> & spans)46 std::vector<struct ScoredSpan> LineBreaker::GenerateScoreSpans(const std::vector<VariantSpan> &spans)
47 {
48     LOGSCOPED(sl, LOGEX_FUNC_LINE_DEBUG(), "GenerateScoreSpans");
49     std::vector<struct ScoredSpan> scoredSpans = {{}};
50     double offset = 0;
51     double preBreak = 0.0;
52     double postBreak = 0.0;
53     CharGroups lastcgs;
54     for (const auto &span : spans) {
55         if (span == nullptr) {
56             throw TEXGINE_EXCEPTION(INVALID_ARGUMENT);
57         }
58 
59         if (auto ts = span.TryToTextSpan(); ts != nullptr) {
60             if (lastcgs.IsSameCharGroups(ts->cgs_) == false) {
61                 // not same word break
62                 offset = scoredSpans.back().postBreak;
63                 lastcgs = ts->cgs_;
64             }
65             preBreak = ts->GetPreBreak();
66             postBreak = ts->GetPostBreak();
67         }
68 
69         if (auto as = span.TryToAnySpan(); as != nullptr) {
70             offset = scoredSpans.back().postBreak;
71             preBreak = as->GetWidth();
72             postBreak = preBreak;
73             // prevent after as is a SameCharGroups ts with before this as
74             lastcgs = {};
75         }
76 
77         LOGEX_FUNC_LINE_DEBUG() << "[" << scoredSpans.size() << "]"
78             << ": offset: " << offset
79             << ", preBreak: " << preBreak
80             << ", postBreak: " << postBreak;
81         scoredSpans.push_back({
82             .span = span,
83             .preBreak = offset + preBreak,
84             .postBreak = offset + postBreak,
85             .score = 0,
86             .prev = 0,
87         });
88     }
89     scoredSpans.erase(scoredSpans.begin());
90     return scoredSpans;
91 }
92 
GetIndent(const double widthLimit,const int index,const std::vector<float> & indents)93 static double GetIndent(const double widthLimit, const int index, const std::vector<float> &indents)
94 {
95     double indent = 0.0;
96     if (indents.size() > 0 && index < static_cast<int>(indents.size())) {
97         indent = indents[index];
98     } else {
99         indent = indents.size() > 0 ? indents.back() : 0.0;
100     }
101 
102     return indent;
103 }
104 
DoBreakLines(std::vector<struct ScoredSpan> & scoredSpans,const double widthLimit,const TypographyStyle & tstyle,const std::vector<float> & indents)105 void LineBreaker::DoBreakLines(std::vector<struct ScoredSpan> &scoredSpans, const double widthLimit,
106     const TypographyStyle &tstyle, const std::vector<float> &indents)
107 {
108     LOGSCOPED(sl, LOGEX_FUNC_LINE_DEBUG(), "UpadateLineBreaksData");
109     scoredSpans.emplace(scoredSpans.cbegin());
110     int index = 0;
111     for (size_t i = 1; i < scoredSpans.size(); i++) {
112         auto &is = scoredSpans[i];
113         is.prev = scoredSpans[i - 1].prev;
114         double newWidthLimit = widthLimit - GetIndent(widthLimit, index, indents);
115         LOGEX_FUNC_LINE_DEBUG() << "[" << i << "]: is.preBreak: " << is.preBreak
116             << ", prev.postBreak: " << scoredSpans[is.prev].postBreak;
117         if (scoredSpans[i].span.IsHardBreak()) {
118             is.prev = static_cast<int>(i - 1);
119             index = 0;
120         }
121 
122         if (FLOATING_GT(is.preBreak - scoredSpans[is.prev].postBreak, newWidthLimit)) {
123             is.prev = static_cast<int>(i - 1);
124             index++;
125             LOGEX_FUNC_LINE_DEBUG() << "  -> [" << is.prev
126                 << "]: prev.postBreak: " << scoredSpans[is.prev].postBreak;
127         }
128 
129         if (tstyle.breakStrategy == BreakStrategy::GREEDY) {
130             continue;
131         }
132 
133         LOGSCOPED(sl1, LOGEX_FUNC_LINE_DEBUG(), "algo");
134         double delta = newWidthLimit - (is.preBreak - scoredSpans[is.prev].postBreak);
135         is.score = delta * delta + scoredSpans[is.prev].score;
136 
137         std::stringstream ss;
138         ss << i;
139         LOGSCOPED(sl, LOGEX_FUNC_LINE_DEBUG(), ss.str());
140         for (size_t j = 0; j < i; j++) {
141             const auto &js = scoredSpans[j];
142             double jdelta = newWidthLimit - (is.preBreak - js.postBreak);
143             if (jdelta < 0) {
144                 continue;
145             }
146 
147             double jscore = js.score + jdelta * jdelta;
148             LOGEX_FUNC_LINE_DEBUG() << "[" << j << "]"
149                 << " s(" << jscore << ")" << " = js(" << js.score << ")"
150                 << " + dd(" << jdelta * jdelta << ")" << " (jdelta: " << jdelta << ")";
151 
152             if (jscore < is.score) {
153                 is.score = jscore;
154                 is.prev = static_cast<int>(j);
155             }
156         }
157         LOGEX_FUNC_LINE_DEBUG() << "[" << i << "] Any{" << is.prev << "<-" << " b(" << is.preBreak << ", "
158                       << is.postBreak << ")" << " s(" << is.score << ")}";
159     }
160     scoredSpans.erase(scoredSpans.begin());
161 }
162 
GenerateBreaks(std::vector<VariantSpan> & spans,const std::vector<struct ScoredSpan> & scoredSpans)163 std::vector<int32_t> LineBreaker::GenerateBreaks(std::vector<VariantSpan> &spans,
164     const std::vector<struct ScoredSpan> &scoredSpans)
165 {
166     LOGSCOPED(sl, LOGEX_FUNC_LINE_DEBUG(), "GenerateBreaks");
167 
168     std::vector<int32_t> lineBreaks;
169     for (int i = static_cast<int>(scoredSpans.size()); i > 0; i = scoredSpans[i - 1].prev) {
170         if (scoredSpans[i - 1].prev >= i) {
171             throw TEXGINE_EXCEPTION(ERROR_STATUS);
172         }
173 
174         LOGEX_FUNC_LINE_DEBUG() << "break at " << i;
175         lineBreaks.push_back(i);
176     }
177     std::reverse(lineBreaks.begin(), lineBreaks.end());
178     for (size_t i = 0; i < spans.size(); i++) {
179         if (spans[i].IsHardBreak()) {
180             auto it = find(lineBreaks.begin(), lineBreaks.end(), i);
181             if (it == lineBreaks.end()) {
182                 lineBreaks.push_back(i);
183             }
184 
185             auto next = find(lineBreaks.begin(), lineBreaks.end(), (i + 1));
186             if (next == lineBreaks.end()) {
187                 lineBreaks.push_back(i + 1);
188             }
189         }
190     }
191     std::sort(lineBreaks.begin(), lineBreaks.end(), [](int32_t lb1, const int32_t lb2) {
192         return (lb1 < lb2);
193     });
194     return lineBreaks;
195 }
196 
GenerateLineMetrics(const double widthLimit,std::vector<VariantSpan> & spans,std::vector<int32_t> & breaks,const std::vector<float> & indents)197 std::vector<LineMetrics> LineBreaker::GenerateLineMetrics(const double widthLimit, std::vector<VariantSpan> &spans,
198     std::vector<int32_t> &breaks, const std::vector<float> &indents)
199 {
200     LOGSCOPED(sl, LOGEX_FUNC_LINE_DEBUG(), "GenerateLineMetrics");
201     LOGEX_FUNC_LINE_DEBUG() << "breaks.size(): " << breaks.size();
202 
203     std::vector<LineMetrics> lineMetrics;
204     if (!breaks.empty() && breaks.back() > static_cast<int>(spans.size())) {
205         throw TEXGINE_EXCEPTION(OUT_OF_RANGE);
206     }
207 
208     if (breaks.empty()) {
209         return {};
210     }
211     if (breaks.front() != 0) {
212         breaks.insert(breaks.begin(), 0);
213     }
214 
215     int32_t prev = breaks[0];
216     int32_t index = 0;
217     for (size_t i = 1; i < breaks.size(); i++) {
218         std::vector<VariantSpan> vss;
219         int32_t next = breaks[i];
220         if (next <= prev) {
221             return {};
222         }
223 
224         for (; prev < next; prev++) {
225             vss.push_back(spans[prev]);
226             if (spans[prev].IsHardBreak()) {
227                 index = 0;
228             }
229         }
230         double indent = GetIndent(widthLimit, index, indents);
231         lineMetrics.push_back({
232             .lineSpans = vss,
233             .indent = indent,
234         });
235         prev = next;
236         index++;
237     }
238 
239     return lineMetrics;
240 }
241 
ProcessHardBreak(std::vector<LineMetrics> & lineMetrics)242 void LineBreaker::ProcessHardBreak(std::vector<LineMetrics> &lineMetrics)
243 {
244     bool isAllHardBreak = false;
245     int lineCount = static_cast<int>(lineMetrics.size());
246     // If the number of lines equal 1 and the char is hard break, add a new line.
247     if (lineCount == 1 && lineMetrics.back().lineSpans.back().IsHardBreak()) {
248         isAllHardBreak = true;
249         // When the number of lines more than 1, and the text ending with two hard breaks, add a new line.
250     } else if (lineCount > 1) {
251         // 1 is the last line
252         isAllHardBreak = lineMetrics[lineCount - 1].lineSpans.front().IsHardBreak() &&
253             lineMetrics[lineCount - 2].lineSpans.back().IsHardBreak(); // 2 is the penultimate line
254     }
255 
256     if (isAllHardBreak) {
257         lineMetrics.push_back(lineMetrics.back());
258     }
259 
260      // lineMetrics.size() - 2 means more than 2 rows are processed
261     for (auto i = 0; i < static_cast<int>(lineMetrics.size() - 2); i++) {
262         if (!lineMetrics[i].lineSpans.back().IsHardBreak() &&
263                 lineMetrics[i + 1].lineSpans.front().IsHardBreak()) {
264             lineMetrics[i].lineSpans.push_back(lineMetrics[i + 1].lineSpans.front());
265             lineMetrics[i + 1].lineSpans.erase(lineMetrics[i + 1].lineSpans.begin());
266         }
267         if (lineMetrics[i + 1].lineSpans.empty()) {
268             lineMetrics.erase(lineMetrics.begin() + (i + 1));
269         }
270     }
271 }
272 } // namespace TextEngine
273 } // namespace Rosen
274 } // namespace OHOS
275