1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <memory>
18
19 #include <gtest/gtest.h>
20
21 #include "minikin/Hyphenator.h"
22
23 #include "FileUtils.h"
24 #include "FontTestUtils.h"
25 #include "GreedyLineBreaker.h"
26 #include "HyphenatorMap.h"
27 #include "LineBreakerTestHelper.h"
28 #include "LocaleListCache.h"
29 #include "MinikinInternal.h"
30 #include "UnicodeUtils.h"
31 #include "WordBreaker.h"
32
33 namespace minikin {
34 namespace {
35
36 using line_breaker_test_helper::ConstantRun;
37 using line_breaker_test_helper::LineBreakExpectation;
38 using line_breaker_test_helper::RectangleLineWidth;
39 using line_breaker_test_helper::sameLineBreak;
40 using line_breaker_test_helper::toString;
41
42 // The ascent/descent of Ascii.ttf with text size = 10.
43 constexpr float ASCENT = -80.0f;
44 constexpr float DESCENT = 20.0f;
45
46 // The ascent/descent of CustomExtent.ttf with text size = 10.
47 constexpr float CUSTOM_ASCENT = -160.0f;
48 constexpr float CUSTOM_DESCENT = 40.0f;
49
50 class GreedyLineBreakerTest : public testing::Test {
51 public:
GreedyLineBreakerTest()52 GreedyLineBreakerTest() {}
53
~GreedyLineBreakerTest()54 virtual ~GreedyLineBreakerTest() {}
55
SetUp()56 virtual void SetUp() override {
57 mHyphenationPattern = readWholeFile("/system/usr/hyphen-data/hyph-en-us.hyb");
58 Hyphenator* hyphenator = Hyphenator::loadBinary(
59 mHyphenationPattern.data(), 2 /* min prefix */, 2 /* min suffix */, "en-US");
60 HyphenatorMap::add("en-US", hyphenator);
61 HyphenatorMap::add("pl", Hyphenator::loadBinary(nullptr, 0, 0, "pl"));
62 }
63
TearDown()64 virtual void TearDown() override { HyphenatorMap::clear(); }
65
66 protected:
doLineBreak(const U16StringPiece & textBuffer,bool doHyphenation,float lineWidth)67 LineBreakResult doLineBreak(const U16StringPiece& textBuffer, bool doHyphenation,
68 float lineWidth) {
69 return doLineBreak(textBuffer, doHyphenation, "en-US", lineWidth);
70 }
71
doLineBreak(const U16StringPiece & textBuffer,bool doHyphenation,const std::string & lang,float lineWidth)72 LineBreakResult doLineBreak(const U16StringPiece& textBuffer, bool doHyphenation,
73 const std::string& lang, float lineWidth) {
74 MeasuredTextBuilder builder;
75 auto family1 = buildFontFamily("Ascii.ttf");
76 auto family2 = buildFontFamily("CustomExtent.ttf");
77 std::vector<std::shared_ptr<FontFamily>> families = {family1, family2};
78 auto fc = std::make_shared<FontCollection>(families);
79 MinikinPaint paint(fc);
80 paint.size = 10.0f; // Make 1em=10px
81 paint.localeListId = LocaleListCache::getId(lang);
82 builder.addStyleRun(0, textBuffer.size(), std::move(paint), false);
83 std::unique_ptr<MeasuredText> measuredText =
84 builder.build(textBuffer, false /* compute hyphenation */,
85 false /* compute full layout */, nullptr /* no hint */);
86 RectangleLineWidth rectangleLineWidth(lineWidth);
87 TabStops tabStops(nullptr, 0, 10);
88 return breakLineGreedy(textBuffer, *measuredText, rectangleLineWidth, tabStops,
89 doHyphenation);
90 }
91
92 private:
93 std::vector<uint8_t> mHyphenationPattern;
94 };
95
TEST_F(GreedyLineBreakerTest,testBreakWithoutHyphenation)96 TEST_F(GreedyLineBreakerTest, testBreakWithoutHyphenation) {
97 constexpr bool NO_HYPHEN = false; // No hyphenation in this test case.
98 const std::vector<uint16_t> textBuf = utf8ToUtf16("This is an example text.");
99
100 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
101 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
102 // Note that disable clang-format everywhere since aligned expectation is more readable.
103 {
104 constexpr float LINE_WIDTH = 1000;
105 std::vector<LineBreakExpectation> expect = {
106 {"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
107 };
108
109 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
110 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
111 << " vs " << std::endl
112 << toString(textBuf, actual);
113 }
114 {
115 constexpr float LINE_WIDTH = 240;
116 std::vector<LineBreakExpectation> expect = {
117 {"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
118 };
119
120 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
121 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
122 << " vs " << std::endl
123 << toString(textBuf, actual);
124 }
125 {
126 constexpr float LINE_WIDTH = 230;
127 // clang-format off
128 std::vector<LineBreakExpectation> expect = {
129 { "This is an example ", 180, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
130 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
131 };
132 // clang-format on
133
134 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
135 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
136 << " vs " << std::endl
137 << toString(textBuf, actual);
138 }
139 {
140 constexpr float LINE_WIDTH = 80;
141 // clang-format off
142 std::vector<LineBreakExpectation> expect = {
143 { "This is ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
144 { "an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
145 { "example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
146 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
147 };
148 // clang-format on
149
150 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
151 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
152 << " vs " << std::endl
153 << toString(textBuf, actual);
154 }
155 {
156 constexpr float LINE_WIDTH = 70;
157 // clang-format off
158 std::vector<LineBreakExpectation> expect = {
159 { "This is ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
160 { "an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
161 { "example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
162 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
163 };
164 // clang-format on
165
166 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
167 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
168 << " vs " << std::endl
169 << toString(textBuf, actual);
170 }
171 {
172 constexpr float LINE_WIDTH = 60;
173 // clang-format off
174 std::vector<LineBreakExpectation> expect = {
175 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
176 { "is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
177 { "exampl", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
178 { "e " , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
179 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
180 };
181 // clang-format on
182
183 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
184 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
185 << " vs " << std::endl
186 << toString(textBuf, actual);
187 }
188 {
189 constexpr float LINE_WIDTH = 50;
190 // clang-format off
191 std::vector<LineBreakExpectation> expect = {
192 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
193 { "is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
194 { "examp" , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
195 { "le " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
196 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
197 };
198 // clang-format on
199
200 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
201 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
202 << " vs " << std::endl
203 << toString(textBuf, actual);
204 }
205 {
206 constexpr float LINE_WIDTH = 40;
207 // clang-format off
208 std::vector<LineBreakExpectation> expect = {
209 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
210 { "is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
211 { "an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
212 { "exam" , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
213 { "ple " , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
214 { "text" , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
215 { "." , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
216 };
217 // clang-format on
218
219 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
220 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
221 << " vs " << std::endl
222 << toString(textBuf, actual);
223 }
224 {
225 constexpr float LINE_WIDTH = 30;
226 // clang-format off
227 std::vector<LineBreakExpectation> expect = {
228 { "Thi" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
229 { "s " , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
230 { "is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
231 { "an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
232 { "exa" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
233 { "mpl" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
234 { "e " , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
235 { "tex" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
236 { "t." , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
237 };
238 // clang-format on
239
240 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
241 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
242 << " vs " << std::endl
243 << toString(textBuf, actual);
244 }
245 {
246 constexpr float LINE_WIDTH = 20;
247 // clang-format off
248 std::vector<LineBreakExpectation> expect = {
249 { "Th" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
250 { "is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
251 { "is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
252 { "an ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
253 { "ex" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
254 { "am" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
255 { "pl" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
256 { "e " , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
257 { "te" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
258 { "xt" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
259 { "." , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
260 };
261 // clang-format on
262
263 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
264 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
265 << " vs " << std::endl
266 << toString(textBuf, actual);
267 }
268 {
269 constexpr float LINE_WIDTH = 10;
270 // clang-format off
271 std::vector<LineBreakExpectation> expect = {
272 { "T" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
273 { "h" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
274 { "i" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
275 { "s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
276 { "i" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
277 { "s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
278 { "a" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
279 { "n ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
280 { "e" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
281 { "x" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
282 { "a" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
283 { "m" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
284 { "p" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
285 { "l" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
286 { "e ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
287 { "t" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
288 { "e" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
289 { "x" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
290 { "t" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
291 { "." , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
292 };
293 // clang-format on
294
295 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
296 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
297 << " vs " << std::endl
298 << toString(textBuf, actual);
299 }
300 }
301
TEST_F(GreedyLineBreakerTest,testBreakWithHyphenation)302 TEST_F(GreedyLineBreakerTest, testBreakWithHyphenation) {
303 constexpr bool NO_HYPHEN = true; // Do hyphenation in this test case.
304 // "hyphenation" is hyphnated to "hy-phen-a-tion".
305 const std::vector<uint16_t> textBuf = utf8ToUtf16("Hyphenation is hyphenation.");
306
307 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
308 constexpr EndHyphenEdit END_HYPHEN = EndHyphenEdit::INSERT_HYPHEN;
309 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
310
311 // Note that disable clang-format everywhere since aligned expectation is more readable.
312 {
313 constexpr float LINE_WIDTH = 1000;
314 std::vector<LineBreakExpectation> expect = {
315 {"Hyphenation is hyphenation.", 270, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT,
316 DESCENT},
317 };
318
319 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
320 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
321 << " vs " << std::endl
322 << toString(textBuf, actual);
323 }
324 {
325 constexpr float LINE_WIDTH = 270;
326 std::vector<LineBreakExpectation> expect = {
327 {"Hyphenation is hyphenation.", 270, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT,
328 DESCENT},
329 };
330
331 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
332 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
333 << " vs " << std::endl
334 << toString(textBuf, actual);
335 }
336 {
337 constexpr float LINE_WIDTH = 260;
338 // clang-format off
339 std::vector<LineBreakExpectation> expect = {
340 { "Hyphenation is " , 140, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
341 { "hyphenation." , 120, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
342 };
343 // clang-format on
344
345 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
346 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
347 << " vs " << std::endl
348 << toString(textBuf, actual);
349 }
350 {
351 constexpr float LINE_WIDTH = 170;
352 // clang-format off
353 std::vector<LineBreakExpectation> expect = {
354 { "Hyphenation is " , 140, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
355 { "hyphenation." , 120, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
356 };
357 // clang-format on
358
359 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
360 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
361 << " vs " << std::endl
362 << toString(textBuf, actual);
363 }
364 {
365 constexpr float LINE_WIDTH = 120;
366 // clang-format off
367 std::vector<LineBreakExpectation> expect = {
368 { "Hyphenation " , 110, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
369 { "is " , 20 , NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
370 { "hyphenation." , 120, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
371 };
372 // clang-format on
373
374 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
375 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
376 << " vs " << std::endl
377 << toString(textBuf, actual);
378 }
379 {
380 constexpr float LINE_WIDTH = 100;
381 // clang-format off
382 std::vector<LineBreakExpectation> expect = {
383 { "Hyphena-", 80, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
384 { "tion is ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
385 { "hyphena-", 80, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
386 { "tion." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
387 };
388 // clang-format on
389
390 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
391 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
392 << " vs " << std::endl
393 << toString(textBuf, actual);
394 }
395 {
396 constexpr float LINE_WIDTH = 80;
397 // clang-format off
398 std::vector<LineBreakExpectation> expect = {
399 { "Hyphena-", 80, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
400 { "tion is ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
401 { "hyphena-", 80, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
402 { "tion." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
403 };
404 // clang-format on
405
406 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
407 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
408 << " vs " << std::endl
409 << toString(textBuf, actual);
410 }
411 {
412 constexpr float LINE_WIDTH = 70;
413 // clang-format off
414 std::vector<LineBreakExpectation> expect = {
415 { "Hyphen-", 70, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
416 { "ation " , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
417 { "is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
418 { "hyphen-", 70, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
419 { "ation." , 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
420 };
421 // clang-format on
422
423 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
424 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
425 << " vs " << std::endl
426 << toString(textBuf, actual);
427 }
428 {
429 constexpr float LINE_WIDTH = 60;
430 // clang-format off
431 std::vector<LineBreakExpectation> expect = {
432 { "Hy-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
433 { "phena-", 60, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
434 { "tion " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
435 { "is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
436 { "hy-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
437 { "phena-", 60, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
438 { "tion." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
439 };
440 // clang-format on
441
442 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
443 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
444 << " vs " << std::endl
445 << toString(textBuf, actual);
446 }
447 {
448 constexpr float LINE_WIDTH = 50;
449 // clang-format off
450 std::vector<LineBreakExpectation> expect = {
451 { "Hy-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
452 { "phen-" , 50, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
453 { "ation ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
454 { "is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
455 { "hy-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
456 { "phen-" , 50, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
457 { "a-" , 20, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
458 { "tion." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
459 };
460 // clang-format on
461
462 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
463 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
464 << " vs " << std::endl
465 << toString(textBuf, actual);
466 }
467 {
468 constexpr float LINE_WIDTH = 40;
469 // clang-format off
470 std::vector<LineBreakExpectation> expect = {
471 { "Hy-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
472 { "phen" , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
473 { "a-" , 20, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
474 { "tion ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
475 { "is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
476 { "hy-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
477 { "phen" , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
478 { "a-" , 20, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
479 { "tion" , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
480 { "." , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
481 };
482 // clang-format on
483
484 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
485 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
486 << " vs " << std::endl
487 << toString(textBuf, actual);
488 }
489 {
490 constexpr float LINE_WIDTH = 30;
491 // clang-format off
492 std::vector<LineBreakExpectation> expect = {
493 { "Hy-", 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
494 { "phe", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
495 { "na-", 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
496 { "tio", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
497 { "n " , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
498 { "is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
499 { "hy-", 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
500 { "phe", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
501 { "na-", 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
502 { "tio", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
503 { "n." , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
504 };
505 // clang-format on
506
507 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
508 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
509 << " vs " << std::endl
510 << toString(textBuf, actual);
511 }
512 {
513 constexpr float LINE_WIDTH = 20;
514 // clang-format off
515 std::vector<LineBreakExpectation> expect = {
516 { "Hy" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
517 { "ph" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
518 { "en" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
519 { "a-" , 20, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
520 { "ti" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
521 { "on ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
522 { "is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
523 { "hy" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
524 { "ph" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
525 { "en" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
526 { "a-" , 20, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
527 { "ti" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
528 { "on" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
529 { "." , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
530 };
531 // clang-format on
532
533 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
534 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
535 << " vs " << std::endl
536 << toString(textBuf, actual);
537 }
538 {
539 constexpr float LINE_WIDTH = 10;
540 // clang-format off
541 std::vector<LineBreakExpectation> expect = {
542 { "H" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
543 { "y" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
544 { "p" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
545 { "h" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
546 { "e" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
547 { "n" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
548 { "a" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
549 { "t" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
550 { "i" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
551 { "o" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
552 { "n ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
553 { "i" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
554 { "s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
555 { "h" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
556 { "y" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
557 { "p" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
558 { "h" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
559 { "e" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
560 { "n" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
561 { "a" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
562 { "t" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
563 { "i" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
564 { "o" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
565 { "n" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
566 { "." , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
567 };
568 // clang-format on
569
570 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
571 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
572 << " vs " << std::endl
573 << toString(textBuf, actual);
574 }
575 }
576
TEST_F(GreedyLineBreakerTest,testHyphenationStartLineChange)577 TEST_F(GreedyLineBreakerTest, testHyphenationStartLineChange) {
578 constexpr bool DO_HYPHEN = true; // Do hyphenation in this test case.
579 // "hyphenation" is hyphnated to "hy-phen-a-tion".
580 const std::vector<uint16_t> textBuf = utf8ToUtf16("czerwono-niebieska");
581
582 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
583 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
584 constexpr StartHyphenEdit START_HYPHEN = StartHyphenEdit::INSERT_HYPHEN;
585
586 // Note that disable clang-format everywhere since aligned expectation is more readable.
587 {
588 constexpr float LINE_WIDTH = 1000;
589 std::vector<LineBreakExpectation> expect = {
590 {"czerwono-niebieska", 180, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
591 };
592
593 const auto actual = doLineBreak(textBuf, DO_HYPHEN, "pl", LINE_WIDTH);
594 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
595 << " vs " << std::endl
596 << toString(textBuf, actual);
597 }
598 {
599 constexpr float LINE_WIDTH = 180;
600 std::vector<LineBreakExpectation> expect = {
601 {"czerwono-niebieska", 180, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
602 };
603
604 const auto actual = doLineBreak(textBuf, DO_HYPHEN, "pl", LINE_WIDTH);
605 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
606 << " vs " << std::endl
607 << toString(textBuf, actual);
608 }
609 {
610 constexpr float LINE_WIDTH = 130;
611 // clang-format off
612 std::vector<LineBreakExpectation> expect = {
613 {"czerwono-" , 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
614 {"-niebieska", 100, START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
615 };
616 // clang-format on
617
618 const auto actual = doLineBreak(textBuf, DO_HYPHEN, "pl", LINE_WIDTH);
619 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
620 << " vs " << std::endl
621 << toString(textBuf, actual);
622 }
623 }
624
TEST_F(GreedyLineBreakerTest,testZeroWidthLine)625 TEST_F(GreedyLineBreakerTest, testZeroWidthLine) {
626 constexpr bool DO_HYPHEN = true; // Do hyphenation in this test case.
627 constexpr float LINE_WIDTH = 0;
628
629 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
630 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
631
632 {
633 const auto textBuf = utf8ToUtf16("");
634 std::vector<LineBreakExpectation> expect = {};
635 const auto actual = doLineBreak(textBuf, DO_HYPHEN, LINE_WIDTH);
636 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
637 << " vs " << std::endl
638 << toString(textBuf, actual);
639 }
640 {
641 const auto textBuf = utf8ToUtf16("A");
642 std::vector<LineBreakExpectation> expect = {
643 {"A", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
644 };
645 const auto actual = doLineBreak(textBuf, DO_HYPHEN, LINE_WIDTH);
646 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
647 << " vs " << std::endl
648 << toString(textBuf, actual);
649 }
650 {
651 const auto textBuf = utf8ToUtf16("AB");
652 std::vector<LineBreakExpectation> expect = {
653 {"A", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
654 {"B", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
655 };
656 const auto actual = doLineBreak(textBuf, DO_HYPHEN, LINE_WIDTH);
657 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
658 << " vs " << std::endl
659 << toString(textBuf, actual);
660 }
661 }
662
TEST_F(GreedyLineBreakerTest,testZeroWidthCharacter)663 TEST_F(GreedyLineBreakerTest, testZeroWidthCharacter) {
664 constexpr float CHAR_WIDTH = 0.0;
665 constexpr bool DO_HYPHEN = true; // Do hyphenation in this test case.
666
667 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
668 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
669 {
670 constexpr float LINE_WIDTH = 1.0;
671 const auto textBuf = utf8ToUtf16("This is an example text.");
672 std::vector<LineBreakExpectation> expect = {
673 {"This is an example text.", 0, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
674 };
675
676 MeasuredTextBuilder builder;
677 builder.addCustomRun<ConstantRun>(Range(0, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
678 DESCENT);
679 std::unique_ptr<MeasuredText> measuredText =
680 builder.build(textBuf, false /* compute hyphenation */,
681 false /* compute full layout */, nullptr /* no hint */);
682 RectangleLineWidth rectangleLineWidth(LINE_WIDTH);
683 TabStops tabStops(nullptr, 0, 10);
684 const auto actual =
685 breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN);
686 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
687 << " vs " << std::endl
688 << toString(textBuf, actual);
689 }
690 {
691 constexpr float LINE_WIDTH = 0.0;
692 const auto textBuf = utf8ToUtf16("This is an example text.");
693 std::vector<LineBreakExpectation> expect = {
694 {"This is an example text.", 0, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
695 };
696 MeasuredTextBuilder builder;
697 builder.addCustomRun<ConstantRun>(Range(0, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
698 DESCENT);
699 std::unique_ptr<MeasuredText> measuredText =
700 builder.build(textBuf, false /* compute hyphenation */,
701 false /* compute full layout */, nullptr /* no hint */);
702 RectangleLineWidth rectangleLineWidth(LINE_WIDTH);
703 TabStops tabStops(nullptr, 0, 10);
704 const auto actual =
705 breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN);
706 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
707 << " vs " << std::endl
708 << toString(textBuf, actual);
709 }
710 }
711
TEST_F(GreedyLineBreakerTest,testLocaleSwitchTest)712 TEST_F(GreedyLineBreakerTest, testLocaleSwitchTest) {
713 constexpr float CHAR_WIDTH = 10.0;
714 constexpr bool DO_HYPHEN = true; // Do hyphenation in this test case.
715
716 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
717 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
718
719 constexpr float LINE_WIDTH = 240;
720 const auto textBuf = utf8ToUtf16("This is an example text.");
721 {
722 std::vector<LineBreakExpectation> expect = {
723 {"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
724 };
725
726 MeasuredTextBuilder builder;
727 builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
728 builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
729 DESCENT);
730 std::unique_ptr<MeasuredText> measuredText =
731 builder.build(textBuf, false /* compute hyphenation */,
732 false /* compute full layout */, nullptr /* no hint */);
733 RectangleLineWidth rectangleLineWidth(LINE_WIDTH);
734 TabStops tabStops(nullptr, 0, 0);
735
736 const auto actual =
737 breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN);
738 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
739 << " vs " << std::endl
740 << toString(textBuf, actual);
741 }
742 {
743 std::vector<LineBreakExpectation> expect = {
744 {"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
745 };
746
747 MeasuredTextBuilder builder;
748 builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
749 builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "fr-FR", CHAR_WIDTH, ASCENT,
750 DESCENT);
751 std::unique_ptr<MeasuredText> measuredText =
752 builder.build(textBuf, false /* compute hyphenation */,
753 false /* compute full layout */, nullptr /* no hint */);
754 RectangleLineWidth rectangleLineWidth(LINE_WIDTH);
755 TabStops tabStops(nullptr, 0, 0);
756
757 const auto actual =
758 breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN);
759 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
760 << " vs " << std::endl
761 << toString(textBuf, actual);
762 }
763 }
764
TEST_F(GreedyLineBreakerTest,testEmailOrUrl)765 TEST_F(GreedyLineBreakerTest, testEmailOrUrl) {
766 constexpr bool DO_HYPHEN = true; // Do hyphenation in this test case.
767
768 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
769 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
770 {
771 constexpr float LINE_WIDTH = 240;
772 const auto textBuf = utf8ToUtf16("This is an url: http://a.b");
773 std::vector<LineBreakExpectation> expect = {
774 {"This is an url: ", 150, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
775 {"http://a.b", 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
776 };
777 const auto actual = doLineBreak(textBuf, DO_HYPHEN, LINE_WIDTH);
778 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
779 << " vs " << std::endl
780 << toString(textBuf, actual);
781 }
782 {
783 constexpr float LINE_WIDTH = 240;
784 const auto textBuf = utf8ToUtf16("This is an email: a@example.com");
785 std::vector<LineBreakExpectation> expect = {
786 {"This is an email: ", 170, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
787 {"a@example.com", 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
788 };
789 const auto actual = doLineBreak(textBuf, DO_HYPHEN, LINE_WIDTH);
790 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
791 << " vs " << std::endl
792 << toString(textBuf, actual);
793 }
794 }
795
TEST_F(GreedyLineBreakerTest,testLocaleSwitch_InEmailOrUrl)796 TEST_F(GreedyLineBreakerTest, testLocaleSwitch_InEmailOrUrl) {
797 constexpr float CHAR_WIDTH = 10.0;
798 constexpr bool DO_HYPHEN = true; // Do hyphenation in this test case.
799
800 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
801 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
802
803 constexpr float LINE_WIDTH = 240;
804 {
805 const auto textBuf = utf8ToUtf16("This is an url: http://a.b");
806 std::vector<LineBreakExpectation> expect = {
807 {"This is an url: ", 150, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
808 {"http://a.b", 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
809 };
810
811 MeasuredTextBuilder builder;
812 builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
813 builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "fr-FR", CHAR_WIDTH, ASCENT,
814 DESCENT);
815 std::unique_ptr<MeasuredText> measuredText =
816 builder.build(textBuf, false /* compute hyphenation */,
817 false /* compute full layout */, nullptr /* no hint */);
818 RectangleLineWidth rectangleLineWidth(LINE_WIDTH);
819 TabStops tabStops(nullptr, 0, 0);
820
821 const auto actual =
822 breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN);
823 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
824 << " vs " << std::endl
825 << toString(textBuf, actual);
826 }
827 {
828 const auto textBuf = utf8ToUtf16("This is an email: a@example.com");
829 std::vector<LineBreakExpectation> expect = {
830 {"This is an email: ", 170, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
831 {"a@example.com", 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
832 };
833
834 MeasuredTextBuilder builder;
835 builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
836 builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "fr-FR", CHAR_WIDTH, ASCENT,
837 DESCENT);
838 std::unique_ptr<MeasuredText> measuredText =
839 builder.build(textBuf, false /* compute hyphenation */,
840 false /* compute full layout */, nullptr /* no hint */);
841 RectangleLineWidth rectangleLineWidth(LINE_WIDTH);
842 TabStops tabStops(nullptr, 0, 0);
843
844 const auto actual =
845 breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN);
846 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
847 << " vs " << std::endl
848 << toString(textBuf, actual);
849 }
850 }
851
852 // b/68669534
TEST_F(GreedyLineBreakerTest,CrashFix_Space_Tab)853 TEST_F(GreedyLineBreakerTest, CrashFix_Space_Tab) {
854 constexpr float CHAR_WIDTH = 10.0;
855 constexpr bool DO_HYPHEN = true; // Do hyphenation in this test case.
856
857 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
858 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
859 {
860 constexpr float LINE_WIDTH = 50;
861 const auto textBuf = utf8ToUtf16("a \tb");
862 std::vector<LineBreakExpectation> expect = {
863 {"a \tb", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
864 };
865
866 MeasuredTextBuilder builder;
867 builder.addCustomRun<ConstantRun>(Range(0, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
868 DESCENT);
869 std::unique_ptr<MeasuredText> measuredText =
870 builder.build(textBuf, false /* compute hyphenation */,
871 false /* compute full layout */, nullptr /* no hint */);
872 RectangleLineWidth rectangleLineWidth(LINE_WIDTH);
873 TabStops tabStops(nullptr, 0, CHAR_WIDTH);
874
875 const auto actual =
876 breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN);
877 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
878 << " vs " << std::endl
879 << toString(textBuf, actual);
880 }
881 }
882
TEST_F(GreedyLineBreakerTest,ExtentTest)883 TEST_F(GreedyLineBreakerTest, ExtentTest) {
884 constexpr bool NO_HYPHEN = false; // No hyphenation in this test case.
885 const std::vector<uint16_t> textBuf = utf8ToUtf16("The \u3042\u3044\u3046 is Japanese.");
886
887 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
888 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
889 {
890 constexpr float LINE_WIDTH = 1000;
891 std::vector<LineBreakExpectation> expect = {
892 {"The \u3042\u3044\u3046 is Japanese.", 200, NO_START_HYPHEN, NO_END_HYPHEN,
893 CUSTOM_ASCENT, CUSTOM_DESCENT},
894 };
895
896 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
897 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
898 << " vs " << std::endl
899 << toString(textBuf, actual);
900 }
901 {
902 constexpr float LINE_WIDTH = 200;
903 std::vector<LineBreakExpectation> expect = {
904 {"The \u3042\u3044\u3046 is Japanese.", 200, NO_START_HYPHEN, NO_END_HYPHEN,
905 CUSTOM_ASCENT, CUSTOM_DESCENT},
906 };
907
908 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
909 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
910 << " vs " << std::endl
911 << toString(textBuf, actual);
912 }
913 {
914 constexpr float LINE_WIDTH = 190;
915 std::vector<LineBreakExpectation> expect = {
916 {"The \u3042\u3044\u3046 is ", 100, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT,
917 CUSTOM_DESCENT},
918 {"Japanese.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
919 };
920
921 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
922 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
923 << " vs " << std::endl
924 << toString(textBuf, actual);
925 }
926 {
927 constexpr float LINE_WIDTH = 90;
928 std::vector<LineBreakExpectation> expect = {
929 {"The \u3042\u3044\u3046 ", 70, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT,
930 CUSTOM_DESCENT},
931 {"is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
932 {"Japanese.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
933 };
934
935 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
936 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
937 << " vs " << std::endl
938 << toString(textBuf, actual);
939 }
940 {
941 constexpr float LINE_WIDTH = 50;
942 std::vector<LineBreakExpectation> expect = {
943 {"The \u3042", 50, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
944 {"\u3044\u3046 is ", 50, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT,
945 CUSTOM_DESCENT},
946 {"Japan", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
947 {"ese.", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
948 };
949
950 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
951 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
952 << " vs " << std::endl
953 << toString(textBuf, actual);
954 }
955 {
956 constexpr float LINE_WIDTH = 40;
957 std::vector<LineBreakExpectation> expect = {
958 {"The ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
959 {"\u3042\u3044\u3046 ", 30, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT,
960 CUSTOM_DESCENT},
961 {"is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
962 {"Japa", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
963 {"nese", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
964 {".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
965 };
966
967 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
968 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
969 << " vs " << std::endl
970 << toString(textBuf, actual);
971 }
972 {
973 constexpr float LINE_WIDTH = 20;
974 std::vector<LineBreakExpectation> expect = {
975 {"Th", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
976 {"e ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
977 {"\u3042\u3044", 20, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
978 {"\u3046 ", 10, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
979 {"is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
980 {"Ja", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
981 {"pa", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
982 {"ne", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
983 {"se", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
984 {".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
985 };
986
987 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
988 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
989 << " vs " << std::endl
990 << toString(textBuf, actual);
991 }
992 {
993 constexpr float LINE_WIDTH = 10;
994 std::vector<LineBreakExpectation> expect = {
995 {"T", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
996 {"h", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
997 {"e ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
998 {"\u3042", 10, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
999 {"\u3044", 10, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1000 {"\u3046 ", 10, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1001 {"i", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1002 {"s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1003 {"J", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1004 {"a", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1005 {"p", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1006 {"a", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1007 {"n", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1008 {"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1009 {"s", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1010 {"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1011 {".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1012 };
1013
1014 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
1015 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1016 << " vs " << std::endl
1017 << toString(textBuf, actual);
1018 }
1019 }
1020
TEST_F(GreedyLineBreakerTest,testReplacementSpanNotBreakTest_SingleChar)1021 TEST_F(GreedyLineBreakerTest, testReplacementSpanNotBreakTest_SingleChar) {
1022 constexpr float CHAR_WIDTH = 10.0;
1023 constexpr bool DO_HYPHEN = true; // Do hyphenation in this test case.
1024
1025 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1026 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1027
1028 const auto textBuf = utf8ToUtf16("This is an example \u2639 text.");
1029
1030 // In this test case, assign a replacement run for "U+2639" with 5 times of CHAR_WIDTH.
1031 auto doLineBreak = [=](float width) {
1032 MeasuredTextBuilder builder;
1033 builder.addCustomRun<ConstantRun>(Range(0, 19), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1034 builder.addReplacementRun(19, 20, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1035 builder.addCustomRun<ConstantRun>(Range(20, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
1036 DESCENT);
1037
1038 std::unique_ptr<MeasuredText> measuredText =
1039 builder.build(textBuf, false /* compute hyphenation */,
1040 false /* compute full layout */, nullptr /* no hint */);
1041 RectangleLineWidth rectangleLineWidth(width);
1042 TabStops tabStops(nullptr, 0, 0);
1043 return breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN);
1044 };
1045
1046 {
1047 constexpr float LINE_WIDTH = 100;
1048 // "is an" is a single replacement span. Do not break.
1049 // clang-format off
1050 std::vector<LineBreakExpectation> expect = {
1051 {"This is an ", 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1052 {"example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1053 {"\u2639 ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1054 {"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1055 };
1056 // clang-format on
1057 const auto actual = doLineBreak(LINE_WIDTH);
1058 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1059 << " vs " << std::endl
1060 << toString(textBuf, actual);
1061 }
1062 {
1063 constexpr float LINE_WIDTH = 90;
1064 // "is an" is a single replacement span. Do not break.
1065 // clang-format off
1066 std::vector<LineBreakExpectation> expect = {
1067 {"This is ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1068 {"an ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1069 {"example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1070 {"\u2639 ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1071 {"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1072 };
1073 // clang-format on
1074 const auto actual = doLineBreak(LINE_WIDTH);
1075 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1076 << " vs " << std::endl
1077 << toString(textBuf, actual);
1078 }
1079 {
1080 constexpr float LINE_WIDTH = 10;
1081 // "is an" is a single replacement span. Do not break.
1082 // clang-format off
1083 std::vector<LineBreakExpectation> expect = {
1084 {"T", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1085 {"h", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1086 {"i", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1087 {"s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1088 {"i", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1089 {"s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1090 {"a", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1091 {"n ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1092 {"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1093 {"x", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1094 {"a", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1095 {"m", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1096 {"p", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1097 {"l", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1098 {"e ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1099 // TODO: This should be "\u2639 " since should not count the trailing line end space
1100 {"\u2639", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1101 {" ", 0, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1102 {"t", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1103 {"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1104 {"x", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1105 {"t", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1106 {".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1107 };
1108 // clang-format on
1109 const auto actual = doLineBreak(LINE_WIDTH);
1110 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1111 << " vs " << std::endl
1112 << toString(textBuf, actual);
1113 }
1114 }
1115
TEST_F(GreedyLineBreakerTest,testReplacementSpanNotBreakTest_MultipleChars)1116 TEST_F(GreedyLineBreakerTest, testReplacementSpanNotBreakTest_MultipleChars) {
1117 constexpr float CHAR_WIDTH = 10.0;
1118 constexpr bool DO_HYPHEN = true; // Do hyphenation in this test case.
1119
1120 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1121 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1122
1123 const auto textBuf = utf8ToUtf16("This is an example text.");
1124
1125 // In this test case, assign a replacement run for "is an " with 5 times of CHAR_WIDTH.
1126 auto doLineBreak = [=](float width) {
1127 MeasuredTextBuilder builder;
1128 builder.addCustomRun<ConstantRun>(Range(0, 5), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1129 builder.addReplacementRun(5, 11, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1130 builder.addCustomRun<ConstantRun>(Range(11, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
1131 DESCENT);
1132
1133 std::unique_ptr<MeasuredText> measuredText =
1134 builder.build(textBuf, false /* compute hyphenation */,
1135 false /* compute full layout */, nullptr /* no hint */);
1136 RectangleLineWidth rectangleLineWidth(width);
1137 TabStops tabStops(nullptr, 0, 0);
1138 return breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN);
1139 };
1140
1141 {
1142 constexpr float LINE_WIDTH = 100;
1143 // "is an" is a single replacement span. Do not break.
1144 // clang-format off
1145 std::vector<LineBreakExpectation> expect = {
1146 {"This is an ", 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1147 {"example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1148 {"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1149 };
1150 // clang-format on
1151 const auto actual = doLineBreak(LINE_WIDTH);
1152 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1153 << " vs " << std::endl
1154 << toString(textBuf, actual);
1155 }
1156 {
1157 constexpr float LINE_WIDTH = 90;
1158 // "is an" is a single replacement span. Do not break.
1159 // clang-format off
1160 std::vector<LineBreakExpectation> expect = {
1161 {"This ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1162 {"is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1163 {"example ",70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1164 {"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1165 };
1166 // clang-format on
1167 const auto actual = doLineBreak(LINE_WIDTH);
1168 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1169 << " vs " << std::endl
1170 << toString(textBuf, actual);
1171 }
1172 {
1173 constexpr float LINE_WIDTH = 10;
1174 // "is an" is a single replacement span. Do not break.
1175 // clang-format off
1176 std::vector<LineBreakExpectation> expect = {
1177 {"T", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1178 {"h", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1179 {"i", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1180 {"s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1181 {"is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1182 {"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1183 {"x", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1184 {"a", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1185 {"m", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1186 {"p", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1187 {"l", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1188 {"e ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1189 {"t", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1190 {"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1191 {"x", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1192 {"t", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1193 {".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1194 };
1195 // clang-format on
1196 const auto actual = doLineBreak(LINE_WIDTH);
1197 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1198 << " vs " << std::endl
1199 << toString(textBuf, actual);
1200 }
1201 }
1202
TEST_F(GreedyLineBreakerTest,testReplacementSpanNotBreakTest_CJK)1203 TEST_F(GreedyLineBreakerTest, testReplacementSpanNotBreakTest_CJK) {
1204 constexpr float CHAR_WIDTH = 10.0;
1205 constexpr bool DO_HYPHEN = true; // Do hyphenation in this test case.
1206
1207 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1208 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1209
1210 // Example string: "Today is a sunny day." in Japanese.
1211 const auto textBuf = utf8ToUtf16("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A");
1212
1213 // In this test case, assign a replacement run for "\u6674\u5929" with 5 times of CHAR_WIDTH.
1214 auto doLineBreak = [=](float width) {
1215 MeasuredTextBuilder builder;
1216 builder.addCustomRun<ConstantRun>(Range(0, 3), "ja-JP", CHAR_WIDTH, ASCENT, DESCENT);
1217 builder.addReplacementRun(3, 5, 5 * CHAR_WIDTH, LocaleListCache::getId("ja-JP"));
1218 builder.addCustomRun<ConstantRun>(Range(5, textBuf.size()), "ja-JP", CHAR_WIDTH, ASCENT,
1219 DESCENT);
1220
1221 std::unique_ptr<MeasuredText> measuredText =
1222 builder.build(textBuf, false /* compute hyphenation */,
1223 false /* compute full layout */, nullptr /* no hint */);
1224 RectangleLineWidth rectangleLineWidth(width);
1225 TabStops tabStops(nullptr, 0, 0);
1226 return breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN);
1227 };
1228
1229 {
1230 constexpr float LINE_WIDTH = 100;
1231 // "\u6674\u5929" is a single replacement span. Do not break.
1232 // clang-format off
1233 std::vector<LineBreakExpectation> expect = {
1234 {"\u672C\u65E5\u306F\u6674\u5929\u306A\u308A",
1235 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1236 };
1237 // clang-format on
1238 const auto actual = doLineBreak(LINE_WIDTH);
1239 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1240 << " vs " << std::endl
1241 << toString(textBuf, actual);
1242 }
1243 {
1244 constexpr float LINE_WIDTH = 90;
1245 // "\u6674\u5929" is a single replacement span. Do not break.
1246 // clang-format off
1247 std::vector<LineBreakExpectation> expect = {
1248 {"\u672C\u65E5\u306F\u6674\u5929\u306A",
1249 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1250 {"\u308A",
1251 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1252 };
1253 // clang-format on
1254 const auto actual = doLineBreak(LINE_WIDTH);
1255 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1256 << " vs " << std::endl
1257 << toString(textBuf, actual);
1258 }
1259 {
1260 constexpr float LINE_WIDTH = 80;
1261 // "\u6674\u5929" is a single replacement span. Do not break.
1262 // clang-format off
1263 std::vector<LineBreakExpectation> expect = {
1264 {"\u672C\u65E5\u306F\u6674\u5929",
1265 80, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1266 {"\u306A\u308A",
1267 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1268 };
1269 // clang-format on
1270 const auto actual = doLineBreak(LINE_WIDTH);
1271 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1272 << " vs " << std::endl
1273 << toString(textBuf, actual);
1274 }
1275 {
1276 constexpr float LINE_WIDTH = 70;
1277 // "\u6674\u5929" is a single replacement span. Do not break.
1278 // clang-format off
1279 std::vector<LineBreakExpectation> expect = {
1280 {"\u672C\u65E5\u306F", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1281 {"\u6674\u5929\u306A\u308A", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1282 };
1283 // clang-format on
1284 const auto actual = doLineBreak(LINE_WIDTH);
1285 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1286 << " vs " << std::endl
1287 << toString(textBuf, actual);
1288 }
1289 {
1290 constexpr float LINE_WIDTH = 60;
1291 // "\u6674\u5929" is a single replacement span. Do not break.
1292 // clang-format off
1293 std::vector<LineBreakExpectation> expect = {
1294 {"\u672C\u65E5\u306F", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1295 {"\u6674\u5929\u306A", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1296 {"\u308A", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1297 };
1298 // clang-format on
1299 const auto actual = doLineBreak(LINE_WIDTH);
1300 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1301 << " vs " << std::endl
1302 << toString(textBuf, actual);
1303 }
1304 {
1305 constexpr float LINE_WIDTH = 50;
1306 // "\u6674\u5929" is a single replacement span. Do not break.
1307 // clang-format off
1308 std::vector<LineBreakExpectation> expect = {
1309 {"\u672C\u65E5\u306F", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1310 {"\u6674\u5929", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1311 {"\u306A\u308A", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1312 };
1313 // clang-format on
1314 const auto actual = doLineBreak(LINE_WIDTH);
1315 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1316 << " vs " << std::endl
1317 << toString(textBuf, actual);
1318 }
1319 {
1320 constexpr float LINE_WIDTH = 40;
1321 // "\u6674\u5929" is a single replacement span. Do not break.
1322 // clang-format off
1323 std::vector<LineBreakExpectation> expect = {
1324 {"\u672C\u65E5\u306F", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1325 {"\u6674\u5929", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1326 {"\u306A\u308A", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1327 };
1328 // clang-format on
1329 const auto actual = doLineBreak(LINE_WIDTH);
1330 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1331 << " vs " << std::endl
1332 << toString(textBuf, actual);
1333 }
1334 {
1335 constexpr float LINE_WIDTH = 10;
1336 // "\u6674\u5929" is a single replacement span. Do not break.
1337 // clang-format off
1338 std::vector<LineBreakExpectation> expect = {
1339 {"\u672C", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1340 {"\u65E5", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1341 {"\u306F", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1342 {"\u6674\u5929", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1343 {"\u306A", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1344 {"\u308A", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1345 };
1346 // clang-format on
1347 const auto actual = doLineBreak(LINE_WIDTH);
1348 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1349 << " vs " << std::endl
1350 << toString(textBuf, actual);
1351 }
1352 }
1353
TEST_F(GreedyLineBreakerTest,testReplacementSpanNotBreakTest_with_punctuation)1354 TEST_F(GreedyLineBreakerTest, testReplacementSpanNotBreakTest_with_punctuation) {
1355 constexpr float CHAR_WIDTH = 10.0;
1356 constexpr bool DO_HYPHEN = true; // Do hyphenation in this test case.
1357
1358 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1359 constexpr EndHyphenEdit END_HYPHEN = EndHyphenEdit::INSERT_HYPHEN;
1360 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1361
1362 const auto textBuf = utf8ToUtf16("This (is an) example text.");
1363
1364 // In this test case, assign a replacement run for "U+2639" with 5 times of CHAR_WIDTH.
1365 auto doLineBreak = [=](float width) {
1366 MeasuredTextBuilder builder;
1367 builder.addCustomRun<ConstantRun>(Range(0, 6), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1368 builder.addReplacementRun(6, 11, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1369 builder.addCustomRun<ConstantRun>(Range(11, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
1370 DESCENT);
1371
1372 std::unique_ptr<MeasuredText> measuredText =
1373 builder.build(textBuf, false /* compute hyphenation */,
1374 false /* compute full layout */, nullptr /* no hint */);
1375 RectangleLineWidth rectangleLineWidth(width);
1376 TabStops tabStops(nullptr, 0, 0);
1377 return breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN);
1378 };
1379
1380 {
1381 constexpr float LINE_WIDTH = 1000;
1382 // "is an" is a single replacement span. Do not break.
1383 // clang-format off
1384 std::vector<LineBreakExpectation> expect = {
1385 {"This (is an) example text.",
1386 260, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1387 };
1388 // clang-format on
1389 const auto actual = doLineBreak(LINE_WIDTH);
1390 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1391 << " vs " << std::endl
1392 << toString(textBuf, actual);
1393 }
1394 {
1395 constexpr float LINE_WIDTH = 250;
1396 // "is an" is a single replacement span. Do not break.
1397 // clang-format off
1398 std::vector<LineBreakExpectation> expect = {
1399 {"This (is an) example ", 200, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1400 {"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1401 };
1402 // clang-format on
1403 const auto actual = doLineBreak(LINE_WIDTH);
1404 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1405 << " vs " << std::endl
1406 << toString(textBuf, actual);
1407 }
1408 {
1409 constexpr float LINE_WIDTH = 190;
1410 // "is an" is a single replacement span. Do not break.
1411 // clang-format off
1412 std::vector<LineBreakExpectation> expect = {
1413 {"This (is an) ", 120, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1414 {"example text.", 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1415 };
1416 // clang-format on
1417 const auto actual = doLineBreak(LINE_WIDTH);
1418 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1419 << " vs " << std::endl
1420 << toString(textBuf, actual);
1421 }
1422 {
1423 constexpr float LINE_WIDTH = 120;
1424 // "is an" is a single replacement span. Do not break.
1425 // clang-format off
1426 std::vector<LineBreakExpectation> expect = {
1427 {"This (is an) ", 120, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1428 {"example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1429 {"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1430 };
1431 // clang-format on
1432 const auto actual = doLineBreak(LINE_WIDTH);
1433 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1434 << " vs " << std::endl
1435 << toString(textBuf, actual);
1436 }
1437 {
1438 constexpr float LINE_WIDTH = 110;
1439 // "is an" is a single replacement span. Do not break.
1440 // clang-format off
1441 std::vector<LineBreakExpectation> expect = {
1442 {"This ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1443 {"(is an) ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1444 {"example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1445 {"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1446 };
1447 // clang-format on
1448 const auto actual = doLineBreak(LINE_WIDTH);
1449 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1450 << " vs " << std::endl
1451 << toString(textBuf, actual);
1452 }
1453 {
1454 constexpr float LINE_WIDTH = 60;
1455 // "is an" is a single replacement span. Do not break.
1456 // clang-format off
1457 std::vector<LineBreakExpectation> expect = {
1458 {"This ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1459 {"(is an", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1460 {") ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1461 {"exam-", 50, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT},
1462 {"ple ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1463 {"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1464 };
1465 // clang-format on
1466 const auto actual = doLineBreak(LINE_WIDTH);
1467 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1468 << " vs " << std::endl
1469 << toString(textBuf, actual);
1470 }
1471 {
1472 constexpr float LINE_WIDTH = 50;
1473 // "is an" is a single replacement span. Do not break.
1474 // clang-format off
1475 std::vector<LineBreakExpectation> expect = {
1476 {"This ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1477 {"(", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1478 {"is an", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1479 {") ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1480 {"exam-", 50, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT},
1481 {"ple ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1482 {"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1483 };
1484 // clang-format on
1485 const auto actual = doLineBreak(LINE_WIDTH);
1486 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1487 << " vs " << std::endl
1488 << toString(textBuf, actual);
1489 }
1490 {
1491 constexpr float LINE_WIDTH = 40;
1492 // "is an" is a single replacement span. Do not break.
1493 // clang-format off
1494 std::vector<LineBreakExpectation> expect = {
1495 {"This ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1496 {"(", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1497 {"is an", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1498 {") ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1499 {"ex-", 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT},
1500 {"am-", 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT},
1501 {"ple ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1502 {"text", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1503 {".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1504 };
1505 // clang-format on
1506 const auto actual = doLineBreak(LINE_WIDTH);
1507 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1508 << " vs " << std::endl
1509 << toString(textBuf, actual);
1510 }
1511 {
1512 constexpr float LINE_WIDTH = 10;
1513 // "is an" is a single replacement span. Do not break.
1514 // clang-format off
1515 std::vector<LineBreakExpectation> expect = {
1516 {"T", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1517 {"h", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1518 {"i", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1519 {"s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1520 {"(", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1521 {"is an", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1522 {") ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1523 {"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1524 {"x", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1525 {"a", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1526 {"m", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1527 {"p", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1528 {"l", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1529 {"e ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1530 {"t", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1531 {"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1532 {"x", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1533 {"t", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1534 {".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1535 };
1536 // clang-format on
1537 const auto actual = doLineBreak(LINE_WIDTH);
1538 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1539 << " vs " << std::endl
1540 << toString(textBuf, actual);
1541 }
1542 }
1543
TEST_F(GreedyLineBreakerTest,testControllCharAfterSpace)1544 TEST_F(GreedyLineBreakerTest, testControllCharAfterSpace) {
1545 constexpr bool NO_HYPHEN = false; // No hyphenation in this test case.
1546 const std::vector<uint16_t> textBuf = utf8ToUtf16("example \u2066example");
1547
1548 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1549 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1550 {
1551 constexpr float LINE_WIDTH = 90;
1552 std::vector<LineBreakExpectation> expect = {
1553 {"example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1554 {"\u2066example", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1555 };
1556
1557 const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
1558 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1559 << " vs " << std::endl
1560 << toString(textBuf, actual);
1561 }
1562 }
1563 } // namespace
1564 } // namespace minikin
1565