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