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