1 /*
2 * Copyright (C) 2018 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 "HyphenatorMap.h"
26 #include "LineBreakerTestHelper.h"
27 #include "LocaleListCache.h"
28 #include "MinikinInternal.h"
29 #include "OptimalLineBreaker.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 OptimalLineBreakerTest : public testing::Test {
51 public:
OptimalLineBreakerTest()52 OptimalLineBreakerTest() {}
53
~OptimalLineBreakerTest()54 virtual ~OptimalLineBreakerTest() {}
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,BreakStrategy strategy,HyphenationFrequency frequency,float lineWidth)67 LineBreakResult doLineBreak(const U16StringPiece& textBuffer, BreakStrategy strategy,
68 HyphenationFrequency frequency, float lineWidth) {
69 return doLineBreak(textBuffer, strategy, frequency, "en-US", lineWidth);
70 }
71
doLineBreak(const U16StringPiece & textBuffer,BreakStrategy strategy,HyphenationFrequency frequency,const std::string & lang,float lineWidth)72 LineBreakResult doLineBreak(const U16StringPiece& textBuffer, BreakStrategy strategy,
73 HyphenationFrequency frequency, const std::string& lang,
74 float lineWidth) {
75 MeasuredTextBuilder builder;
76 auto family1 = buildFontFamily("Ascii.ttf");
77 auto family2 = buildFontFamily("CustomExtent.ttf");
78 std::vector<std::shared_ptr<FontFamily>> families = {family1, family2};
79 auto fc = std::make_shared<FontCollection>(families);
80 MinikinPaint paint(fc);
81 paint.size = 10.0f; // Make 1em=1px
82 paint.localeListId = LocaleListCache::getId(lang);
83 builder.addStyleRun(0, textBuffer.size(), std::move(paint), false);
84 std::unique_ptr<MeasuredText> measuredText =
85 builder.build(textBuffer, true /* compute hyphenation */,
86 false /* compute full layout */, nullptr /* no hint */);
87 return doLineBreak(textBuffer, *measuredText, strategy, frequency, lineWidth);
88 }
89
doLineBreak(const U16StringPiece & textBuffer,const MeasuredText & measuredText,BreakStrategy strategy,HyphenationFrequency frequency,float lineWidth)90 LineBreakResult doLineBreak(const U16StringPiece& textBuffer, const MeasuredText& measuredText,
91 BreakStrategy strategy, HyphenationFrequency frequency,
92 float lineWidth) {
93 RectangleLineWidth rectangleLineWidth(lineWidth);
94 return breakLineOptimal(textBuffer, measuredText, rectangleLineWidth, strategy, frequency,
95 false /* justified */);
96 }
97
98 private:
99 std::vector<uint8_t> mHyphenationPattern;
100 };
101
TEST_F(OptimalLineBreakerTest,testBreakWithoutHyphenation)102 TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
103 constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
104 constexpr BreakStrategy BALANCED = BreakStrategy::Balanced;
105 constexpr HyphenationFrequency NO_HYPHENATION = HyphenationFrequency::None;
106 constexpr HyphenationFrequency NORMAL_HYPHENATION = HyphenationFrequency::Normal;
107 const std::vector<uint16_t> textBuf = utf8ToUtf16("This is an example text.");
108
109 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
110 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
111 constexpr EndHyphenEdit END_HYPHEN = EndHyphenEdit::INSERT_HYPHEN;
112 // Note that disable clang-format everywhere since aligned expectation is more readable.
113 {
114 constexpr float LINE_WIDTH = 1000;
115 std::vector<LineBreakExpectation> expect = {
116 {"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
117 };
118
119 auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
120 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
121 << " vs " << std::endl
122 << toString(textBuf, actual);
123 actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
124 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
125 << " vs " << std::endl
126 << toString(textBuf, actual);
127 actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
128 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
129 << " vs " << std::endl
130 << toString(textBuf, actual);
131 actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
132 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
133 << " vs " << std::endl
134 << toString(textBuf, actual);
135 }
136 {
137 constexpr float LINE_WIDTH = 240;
138 std::vector<LineBreakExpectation> expect = {
139 {"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
140 };
141 auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
142 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
143 << " vs " << std::endl
144 << toString(textBuf, actual);
145 actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
146 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
147 << " vs " << std::endl
148 << toString(textBuf, actual);
149 actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
150 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
151 << " vs " << std::endl
152 << toString(textBuf, actual);
153 actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
154 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
155 << " vs " << std::endl
156 << toString(textBuf, actual);
157 }
158 {
159 constexpr float LINE_WIDTH = 230;
160 // clang-format off
161 std::vector<LineBreakExpectation> expect = {
162 { "This is an example " , 180, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
163 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
164 };
165 // clang-format on
166
167 auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
168 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
169 << " vs " << std::endl
170 << toString(textBuf, actual);
171 actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
172 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
173 << " vs " << std::endl
174 << toString(textBuf, actual);
175
176 // clang-format off
177 expect = {
178 { "This is an " , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
179 { "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
180 };
181 // clang-format on
182 actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
183 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
184 << " vs " << std::endl
185 << toString(textBuf, actual);
186 // clang-format off
187 expect = {
188 { "This is an ex-" , 140, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
189 { "ample text." , 110, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
190 };
191 // clang-format on
192 actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
193 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
194 << " vs " << std::endl
195 << toString(textBuf, actual);
196 }
197 {
198 constexpr float LINE_WIDTH = 170;
199 // clang-format off
200 std::vector<LineBreakExpectation> expect = {
201 { "This is an " , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
202 { "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
203 };
204 // clang-format on
205
206 auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
207 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
208 << " vs " << std::endl
209 << toString(textBuf, actual);
210 // clang-format off
211 expect = {
212 { "This is an exam-" , 160, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
213 { "ple text." , 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
214 };
215 // clang-format on
216 actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
217 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
218 << " vs " << std::endl
219 << toString(textBuf, actual);
220 // clang-format off
221 expect = {
222 { "This is an " , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
223 { "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
224 };
225 // clang-format on
226 actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
227 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
228 << " vs " << std::endl
229 << toString(textBuf, actual);
230 // clang-format off
231 expect = {
232 { "This is an ex-", 140, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
233 { "ample text." , 110, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
234 };
235 // clang-format on
236 actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
237 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
238 << " vs " << std::endl
239 << toString(textBuf, actual);
240 }
241 {
242 constexpr float LINE_WIDTH = 160;
243 // clang-format off
244 std::vector<LineBreakExpectation> expect = {
245 { "This is an " , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
246 { "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
247 };
248 // clang-format on
249
250 auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
251 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
252 << " vs " << std::endl
253 << toString(textBuf, actual);
254 // clang-format off
255 expect = {
256 { "This is an exam-" , 160, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
257 { "ple text." , 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
258 };
259 // clang-format on
260 actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
261 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
262 << " vs " << std::endl
263 << toString(textBuf, actual);
264 // clang-format off
265 expect = {
266 { "This is an " , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
267 { "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
268 };
269 // clang-format on
270 actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
271 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
272 << " vs " << std::endl
273 << toString(textBuf, actual);
274 // clang-format off
275 expect = {
276 { "This is an ex-", 140, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
277 { "ample text." , 110, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
278 };
279 // clang-format on
280 actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
281 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
282 << " vs " << std::endl
283 << toString(textBuf, actual);
284 }
285 {
286 constexpr float LINE_WIDTH = 150;
287 // clang-format off
288 std::vector<LineBreakExpectation> expect = {
289 { "This is an " , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
290 { "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
291 };
292 // clang-format on
293
294 auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
295 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
296 << " vs " << std::endl
297 << toString(textBuf, actual);
298 // clang-format off
299 expect = {
300 { "This is an ex-", 140, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
301 { "ample text." , 110, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
302 };
303 // clang-format on
304 actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
305 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
306 << " vs " << std::endl
307 << toString(textBuf, actual);
308 // clang-format off
309 expect = {
310 { "This is an " , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
311 { "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
312 };
313 // clang-format on
314 actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
315 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
316 << " vs " << std::endl
317 << toString(textBuf, actual);
318 // clang-format off
319 expect = {
320 { "This is an ex-", 140, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
321 { "ample text." , 110, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
322 };
323 // clang-format on
324 actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
325 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
326 << " vs " << std::endl
327 << toString(textBuf, actual);
328 }
329 {
330 constexpr float LINE_WIDTH = 130;
331 // clang-format off
332 std::vector<LineBreakExpectation> expect = {
333 { "This is an " , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
334 { "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
335 };
336 // clang-format on
337
338 auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
339 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
340 << " vs " << std::endl
341 << toString(textBuf, actual);
342 actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
343 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
344 << " vs " << std::endl
345 << toString(textBuf, actual);
346 actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
347 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
348 << " vs " << std::endl
349 << toString(textBuf, actual);
350 actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
351 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
352 << " vs " << std::endl
353 << toString(textBuf, actual);
354 }
355 {
356 constexpr float LINE_WIDTH = 120;
357 // clang-format off
358 std::vector<LineBreakExpectation> expect = {
359 { "This is an ", 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
360 { "example " , 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
361 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
362 };
363 // clang-format on
364
365 auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
366 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
367 << " vs " << std::endl
368 << toString(textBuf, actual);
369 actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
370 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
371 << " vs " << std::endl
372 << toString(textBuf, actual);
373 actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
374 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
375 << " vs " << std::endl
376 << toString(textBuf, actual);
377 // clang-format off
378 expect = {
379 { "This is " , 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
380 { "an exam-" , 80, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
381 { "ple text.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
382 };
383 // clang-format on
384 actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
385 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
386 << " vs " << std::endl
387 << toString(textBuf, actual);
388 }
389 {
390 constexpr float LINE_WIDTH = 90;
391 // clang-format off
392 std::vector<LineBreakExpectation> expect = {
393 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
394 { "is an " , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
395 { "example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
396 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
397 };
398 // clang-format on
399
400 auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
401 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
402 << " vs " << std::endl
403 << toString(textBuf, actual);
404 // clang-format off
405 expect = {
406 { "This is " , 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
407 { "an exam-" , 80, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
408 { "ple text.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
409 };
410 // clang-format on
411 actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
412 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
413 << " vs " << std::endl
414 << toString(textBuf, actual);
415 // clang-format off
416 expect = {
417 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
418 { "is an " , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
419 { "example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
420 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
421 };
422 // clang-format on
423 actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
424 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
425 << " vs " << std::endl
426 << toString(textBuf, actual);
427 // clang-format off
428 expect = {
429 { "This is " , 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
430 { "an exam-" , 80, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
431 { "ple text.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
432 };
433 // clang-format on
434 actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
435 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
436 << " vs " << std::endl
437 << toString(textBuf, actual);
438 }
439 {
440 constexpr float LINE_WIDTH = 80;
441 // clang-format off
442 std::vector<LineBreakExpectation> expect = {
443 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
444 { "is an " , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
445 { "example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
446 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
447 };
448 // clang-format on
449
450 auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
451 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
452 << " vs " << std::endl
453 << toString(textBuf, actual);
454 actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
455 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
456 << " vs " << std::endl
457 << toString(textBuf, actual);
458 // clang-format off
459 expect = {
460 { "This is ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
461 { "an ex-" , 60, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
462 { "ample " , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
463 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
464 };
465 // clang-format on
466 actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
467 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
468 << " vs " << std::endl
469 << toString(textBuf, actual);
470 actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
471 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
472 << " vs " << std::endl
473 << toString(textBuf, actual);
474 }
475 {
476 constexpr float LINE_WIDTH = 70;
477 // clang-format off
478 std::vector<LineBreakExpectation> expect = {
479 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
480 { "is an " , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
481 { "example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
482 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
483 };
484 // clang-format on
485
486 auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
487 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
488 << " vs " << std::endl
489 << toString(textBuf, actual);
490 actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
491 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
492 << " vs " << std::endl
493 << toString(textBuf, actual);
494 // clang-format off
495 expect = {
496 { "This is ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
497 { "an ex-" , 60, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
498 { "ample " , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
499 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
500 };
501 // clang-format on
502 actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
503 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
504 << " vs " << std::endl
505 << toString(textBuf, actual);
506 actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
507 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
508 << " vs " << std::endl
509 << toString(textBuf, actual);
510 }
511 {
512 constexpr float LINE_WIDTH = 60;
513 // clang-format off
514 std::vector<LineBreakExpectation> expect = {
515 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
516 { "is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
517 // TODO: Is this desperate break working correctly?
518 { "exa" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
519 { "mple " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
520 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
521 };
522 // clang-format on
523
524 auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
525 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
526 << " vs " << std::endl
527 << toString(textBuf, actual);
528 actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
529 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
530 << " vs " << std::endl
531 << toString(textBuf, actual);
532 // clang-format off
533 expect = {
534 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
535 { "is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
536 { "exam-" , 50, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
537 { "ple " , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
538 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
539 };
540 // clang-format on
541 actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
542 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
543 << " vs " << std::endl
544 << toString(textBuf, actual);
545 actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
546 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
547 << " vs " << std::endl
548 << toString(textBuf, actual);
549 }
550 {
551 constexpr float LINE_WIDTH = 50;
552 // clang-format off
553 std::vector<LineBreakExpectation> expect = {
554 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
555 { "is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
556 // TODO: Is this desperate break working correctly?
557 { "exa" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
558 { "mple " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
559 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
560 };
561 // clang-format on
562
563 auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
564 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
565 << " vs " << std::endl
566 << toString(textBuf, actual);
567 actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
568 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
569 << " vs " << std::endl
570 << toString(textBuf, actual);
571 // clang-format off
572 expect = {
573 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
574 { "is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
575 { "exam-" , 50, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
576 { "ple " , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
577 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
578 };
579 // clang-format on
580 actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
581 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
582 << " vs " << std::endl
583 << toString(textBuf, actual);
584 actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
585 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
586 << " vs " << std::endl
587 << toString(textBuf, actual);
588 }
589 {
590 constexpr float LINE_WIDTH = 40;
591 // clang-format off
592 std::vector<LineBreakExpectation> expect = {
593 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
594 { "is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
595 { "an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
596 // TODO: Is this desperate break working correctly?
597 { "exa" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
598 { "mple " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
599 { "text" , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
600 { "." , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
601 };
602 // clang-format on
603
604 auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
605 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
606 << " vs " << std::endl
607 << toString(textBuf, actual);
608 // clang-format off
609 expect = {
610 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
611 { "is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
612 { "an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
613 // TODO: Is this desperate break working correctly?
614 { "exa" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
615 { "mple " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
616 // TODO: Is this desperate break working correctly?
617 { "t" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
618 { "ext." , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
619 };
620 // clang-format on
621 actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
622 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
623 << " vs " << std::endl
624 << toString(textBuf, actual);
625 // clang-format off
626 expect = {
627 { "This ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
628 { "is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
629 { "an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
630 { "ex-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
631 { "am-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
632 { "ple " , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
633 { "text" , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
634 { "." , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
635 };
636 // clang-format on
637 actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
638 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
639 << " vs " << std::endl
640 << toString(textBuf, actual);
641 // clang-format off
642 expect = {
643 { "This ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
644 { "is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
645 { "an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
646 { "ex-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
647 { "am-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
648 { "ple " , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
649 // TODO: Is this desperate break working correctly?
650 { "te" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
651 { "xt." , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
652 };
653 // clang-format on
654 actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
655 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
656 << " vs " << std::endl
657 << toString(textBuf, actual);
658 }
659 {
660 constexpr float LINE_WIDTH = 30;
661 // clang-format off
662 std::vector<LineBreakExpectation> expect = {
663 // TODO: Is this desperate break working correctly?
664 { "T" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
665 { "his ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
666 { "is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
667 { "an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
668 // TODO: Is this desperate break working correctly?
669 { "e" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
670 { "xam" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
671 { "ple ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
672 { "tex" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
673 { "t." , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
674 };
675 // clang-format on
676
677 auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
678 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
679 << " vs " << std::endl
680 << toString(textBuf, actual);
681 // clang-format off
682 expect = {
683 // TODO: Is this desperate break working correctly?
684 { "T" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
685 { "his ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
686 { "is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
687 { "an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
688 // TODO: Is this desperate break working correctly?
689 { "e" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
690 { "xam" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
691 { "ple ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
692 // TODO: Is this desperate break working correctly?
693 { "te" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
694 { "xt." , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
695 };
696 // clang-format on
697 actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
698 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
699 << " vs " << std::endl
700 << toString(textBuf, actual);
701 // clang-format off
702 expect = {
703 // TODO: Is this desperate break working correctly?
704 { "T" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
705 { "his ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
706 { "is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
707 { "an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
708 { "ex-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
709 { "am-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
710 { "ple ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
711 { "tex" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
712 { "t." , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
713 };
714 // clang-format on
715 actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
716 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
717 << " vs " << std::endl
718 << toString(textBuf, actual);
719 // clang-format off
720 expect = {
721 // TODO: Is this desperate break working correctly?
722 {"T" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
723 {"his ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
724 {"is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
725 {"an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
726 {"ex-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT},
727 {"am-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT},
728 {"ple ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
729 // TODO: Is this desperate break working correctly?
730 {"te" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
731 {"xt." , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
732 };
733 // clang-format on
734 actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
735 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
736 << " vs " << std::endl
737 << toString(textBuf, actual);
738 }
739 {
740 constexpr float LINE_WIDTH = 20;
741 // clang-format off
742 std::vector<LineBreakExpectation> expect = {
743 { "Th" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
744 { "is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
745 { "is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
746 { "an ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
747 // TODO: Is this desperate break working correctly?
748 { "e" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
749 { "xa" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
750 { "mp" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
751 { "le ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
752 { "te" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
753 { "xt" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
754 { "." , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
755 };
756 // clang-format on
757
758 auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
759 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
760 << " vs " << std::endl
761 << toString(textBuf, actual);
762 actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
763 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
764 << " vs " << std::endl
765 << toString(textBuf, actual);
766 // clang-format off
767 expect = {
768 { "Th" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
769 { "is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
770 { "is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
771 { "an ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
772 // TODO: Is this desperate break working correctly?
773 { "e" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
774 { "xa" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
775 { "mp" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
776 { "le ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
777 // TODO: Is this desperate break working correctly?
778 { "t" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
779 { "ex" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
780 { "t." , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
781 };
782 // clang-format on
783 actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
784 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
785 << " vs " << std::endl
786 << toString(textBuf, actual);
787 actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
788 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
789 << " vs " << std::endl
790 << toString(textBuf, actual);
791 }
792 {
793 constexpr float LINE_WIDTH = 10;
794 // clang-format off
795 std::vector<LineBreakExpectation> expect = {
796 { "T" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
797 { "h" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
798 { "i" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
799 { "s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
800 { "i" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
801 { "s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
802 { "a" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
803 { "n ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
804 { "e" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
805 { "x" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
806 { "a" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
807 { "m" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
808 { "p" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
809 { "l" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
810 { "e ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
811 { "t" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
812 { "e" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
813 { "x" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
814 { "t" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
815 { "." , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
816 };
817 // clang-format on
818
819 auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
820 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
821 << " vs " << std::endl
822 << toString(textBuf, actual);
823 actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
824 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
825 << " vs " << std::endl
826 << toString(textBuf, actual);
827 actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
828 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
829 << " vs " << std::endl
830 << toString(textBuf, actual);
831 actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
832 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
833 << " vs " << std::endl
834 << toString(textBuf, actual);
835 }
836 }
837
TEST_F(OptimalLineBreakerTest,testHyphenationStartLineChange)838 TEST_F(OptimalLineBreakerTest, testHyphenationStartLineChange) {
839 constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
840 constexpr HyphenationFrequency NORMAL_HYPHENATION = HyphenationFrequency::Normal;
841 // "hyphenation" is hyphnated to "hy-phen-a-tion".
842 const std::vector<uint16_t> textBuf = utf8ToUtf16("czerwono-niebieska");
843
844 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
845 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
846 constexpr StartHyphenEdit START_HYPHEN = StartHyphenEdit::INSERT_HYPHEN;
847
848 // Note that disable clang-format everywhere since aligned expectation is more readable.
849 {
850 constexpr float LINE_WIDTH = 1000;
851 std::vector<LineBreakExpectation> expect = {
852 {"czerwono-niebieska", 180, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
853 };
854
855 const auto actual =
856 doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "pl", LINE_WIDTH);
857 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
858 << " vs " << std::endl
859 << toString(textBuf, actual);
860 }
861 {
862 constexpr float LINE_WIDTH = 180;
863 std::vector<LineBreakExpectation> expect = {
864 {"czerwono-niebieska", 180, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
865 };
866
867 const auto actual =
868 doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "pl", LINE_WIDTH);
869 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
870 << " vs " << std::endl
871 << toString(textBuf, actual);
872 }
873 {
874 constexpr float LINE_WIDTH = 130;
875 // clang-format off
876 std::vector<LineBreakExpectation> expect = {
877 {"czerwono-" , 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
878 {"-niebieska", 100, START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
879 };
880 // clang-format on
881
882 const auto actual =
883 doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "pl", LINE_WIDTH);
884 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
885 << " vs " << std::endl
886 << toString(textBuf, actual);
887 }
888 }
889
TEST_F(OptimalLineBreakerTest,testZeroWidthLine)890 TEST_F(OptimalLineBreakerTest, testZeroWidthLine) {
891 constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
892 constexpr HyphenationFrequency NORMAL_HYPHENATION = HyphenationFrequency::Normal;
893 constexpr float LINE_WIDTH = 0;
894
895 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
896 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
897
898 {
899 const auto textBuf = utf8ToUtf16("");
900 std::vector<LineBreakExpectation> expect = {};
901 const auto actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
902 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
903 << " vs " << std::endl
904 << toString(textBuf, actual);
905 }
906 {
907 const auto textBuf = utf8ToUtf16("A");
908 std::vector<LineBreakExpectation> expect = {
909 {"A", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
910 };
911 const auto actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
912 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
913 << " vs " << std::endl
914 << toString(textBuf, actual);
915 }
916 {
917 const auto textBuf = utf8ToUtf16("AB");
918 std::vector<LineBreakExpectation> expect = {
919 {"A", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
920 {"B", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
921 };
922 const auto actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
923 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
924 << " vs " << std::endl
925 << toString(textBuf, actual);
926 }
927 }
928
TEST_F(OptimalLineBreakerTest,testZeroWidthCharacter)929 TEST_F(OptimalLineBreakerTest, testZeroWidthCharacter) {
930 constexpr float CHAR_WIDTH = 0.0;
931 constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
932 constexpr HyphenationFrequency NORMAL_HYPHENATION = HyphenationFrequency::Normal;
933
934 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
935 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
936 {
937 constexpr float LINE_WIDTH = 1.0;
938 const auto textBuf = utf8ToUtf16("This is an example text.");
939 std::vector<LineBreakExpectation> expect = {
940 {"This is an example text.", 0, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
941 };
942 MeasuredTextBuilder builder;
943 builder.addCustomRun<ConstantRun>(Range(0, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
944 DESCENT);
945 std::unique_ptr<MeasuredText> measuredText =
946 builder.build(textBuf, true /* compute hyphenation */,
947 false /* compute full layout */, nullptr /* no hint */);
948
949 const auto actual =
950 doLineBreak(textBuf, *measuredText, HIGH_QUALITY, NORMAL_HYPHENATION, 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 = 0.0;
957 const auto textBuf = utf8ToUtf16("This is an example text.");
958 std::vector<LineBreakExpectation> expect = {
959 {"This is an example text.", 0, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
960 };
961 MeasuredTextBuilder builder;
962 builder.addCustomRun<ConstantRun>(Range(0, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
963 DESCENT);
964 std::unique_ptr<MeasuredText> measuredText =
965 builder.build(textBuf, true /* compute hyphenation */,
966 false /* compute full layout */, nullptr /* no hint */);
967
968 const auto actual =
969 doLineBreak(textBuf, *measuredText, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
970 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
971 << " vs " << std::endl
972 << toString(textBuf, actual);
973 }
974 }
975
TEST_F(OptimalLineBreakerTest,testLocaleSwitchTest)976 TEST_F(OptimalLineBreakerTest, testLocaleSwitchTest) {
977 constexpr float CHAR_WIDTH = 10.0;
978 constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
979 constexpr HyphenationFrequency NORMAL_HYPHENATION = HyphenationFrequency::Normal;
980
981 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
982 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
983
984 constexpr float LINE_WIDTH = 240;
985 const auto textBuf = utf8ToUtf16("This is an example text.");
986 {
987 std::vector<LineBreakExpectation> expect = {
988 {"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
989 };
990
991 MeasuredTextBuilder builder;
992 builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
993 builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
994 DESCENT);
995 std::unique_ptr<MeasuredText> measuredText =
996 builder.build(textBuf, true /* compute hyphenation */,
997 false /* compute full layout */, nullptr /* no hint */);
998
999 const auto actual =
1000 doLineBreak(textBuf, *measuredText, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
1001 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1002 << " vs " << std::endl
1003 << toString(textBuf, actual);
1004 }
1005 {
1006 std::vector<LineBreakExpectation> expect = {
1007 {"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1008 };
1009
1010 MeasuredTextBuilder builder;
1011 builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1012 builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "fr-FR", CHAR_WIDTH, ASCENT,
1013 DESCENT);
1014 std::unique_ptr<MeasuredText> measuredText =
1015 builder.build(textBuf, true /* compute hyphenation */,
1016 false /* compute full layout */, nullptr /* no hint */);
1017 const auto actual =
1018 doLineBreak(textBuf, *measuredText, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
1019 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1020 << " vs " << std::endl
1021 << toString(textBuf, actual);
1022 }
1023 }
1024
TEST_F(OptimalLineBreakerTest,testEmailOrUrl)1025 TEST_F(OptimalLineBreakerTest, testEmailOrUrl) {
1026 constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
1027 constexpr BreakStrategy BALANCED = BreakStrategy::Balanced;
1028 constexpr HyphenationFrequency NO_HYPHENATION = HyphenationFrequency::None;
1029 constexpr HyphenationFrequency NORMAL_HYPHENATION = HyphenationFrequency::Normal;
1030
1031 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1032 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1033 {
1034 constexpr float LINE_WIDTH = 240;
1035 const auto textBuf = utf8ToUtf16("This is an url: http://a.b");
1036 // clang-format off
1037 std::vector<LineBreakExpectation> expect = {
1038 // TODO: Fix this. Prefer not to break inside URL.
1039 {"This is an url: http://a", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1040 {".b", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1041 };
1042 // clang-format on
1043 auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
1044 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1045 << " vs " << std::endl
1046 << toString(textBuf, actual);
1047 actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
1048 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1049 << " vs " << std::endl
1050 << toString(textBuf, actual);
1051 // clang-format off
1052 expect = {
1053 {"This is an url: ", 150, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1054 {"http://a.b", 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1055 };
1056 // clang-format on
1057 actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
1058 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1059 << " vs " << std::endl
1060 << toString(textBuf, actual);
1061 actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
1062 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1063 << " vs " << std::endl
1064 << toString(textBuf, actual);
1065 }
1066 {
1067 constexpr float LINE_WIDTH = 240;
1068 const auto textBuf = utf8ToUtf16("This is an email: a@example.com");
1069 // clang-format off
1070 std::vector<LineBreakExpectation> expect = {
1071 {"This is an email: ", 170, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1072 {"a@example.com" , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1073 };
1074 // clang-format on
1075
1076 auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
1077 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1078 << " vs " << std::endl
1079 << toString(textBuf, actual);
1080 actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
1081 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1082 << " vs " << std::endl
1083 << toString(textBuf, actual);
1084 actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
1085 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1086 << " vs " << std::endl
1087 << toString(textBuf, actual);
1088 actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
1089 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1090 << " vs " << std::endl
1091 << toString(textBuf, actual);
1092 }
1093 }
1094
TEST_F(OptimalLineBreakerTest,testLocaleSwitch_InEmailOrUrl)1095 TEST_F(OptimalLineBreakerTest, testLocaleSwitch_InEmailOrUrl) {
1096 constexpr float CHAR_WIDTH = 10.0;
1097 constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
1098 constexpr BreakStrategy BALANCED = BreakStrategy::Balanced;
1099 constexpr HyphenationFrequency NO_HYPHENATION = HyphenationFrequency::None;
1100 constexpr HyphenationFrequency NORMAL_HYPHENATION = HyphenationFrequency::Normal;
1101
1102 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1103 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1104
1105 constexpr float LINE_WIDTH = 240;
1106 {
1107 const auto textBuf = utf8ToUtf16("This is an url: http://a.b");
1108 MeasuredTextBuilder builder;
1109 builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1110 builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "fr-FR", CHAR_WIDTH, ASCENT,
1111 DESCENT);
1112 std::unique_ptr<MeasuredText> measured =
1113 builder.build(textBuf, true /* compute hyphenation */,
1114 false /* compute full layout */, nullptr /* no hint */);
1115
1116 // clang-format off
1117 std::vector<LineBreakExpectation> expect = {
1118 // TODO: Fix this. Prefer not to break inside URL.
1119 {"This is an url: http://a", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1120 {".b", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1121 };
1122 // clang-format on
1123
1124 auto actual = doLineBreak(textBuf, *measured, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
1125 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1126 << " vs " << std::endl
1127 << toString(textBuf, actual);
1128 actual = doLineBreak(textBuf, *measured, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
1129 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1130 << " vs " << std::endl
1131 << toString(textBuf, actual);
1132
1133 // clang-format off
1134 expect = {
1135 {"This is an url: ", 150, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1136 {"http://a.b", 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1137 };
1138 // clang-format on
1139
1140 actual = doLineBreak(textBuf, *measured, BALANCED, NO_HYPHENATION, LINE_WIDTH);
1141 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1142 << " vs " << std::endl
1143 << toString(textBuf, actual);
1144 actual = doLineBreak(textBuf, *measured, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
1145 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1146 << " vs " << std::endl
1147 << toString(textBuf, actual);
1148 }
1149 {
1150 const auto textBuf = utf8ToUtf16("This is an email: a@example.com");
1151 MeasuredTextBuilder builder;
1152 builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1153 builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "fr-FR", CHAR_WIDTH, ASCENT,
1154 DESCENT);
1155 std::unique_ptr<MeasuredText> measured =
1156 builder.build(textBuf, true /* compute hyphenation */,
1157 false /* compute full layout */, nullptr /* no hint */);
1158
1159 // clang-format off
1160 std::vector<LineBreakExpectation> expect = {
1161 {"This is an email: ", 170, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1162 {"a@example.com", 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1163 };
1164 // clang-format on
1165
1166 auto actual = doLineBreak(textBuf, *measured, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
1167 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1168 << " vs " << std::endl
1169 << toString(textBuf, actual);
1170 actual = doLineBreak(textBuf, *measured, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
1171 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1172 << " vs " << std::endl
1173 << toString(textBuf, actual);
1174 actual = doLineBreak(textBuf, *measured, BALANCED, NO_HYPHENATION, LINE_WIDTH);
1175 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1176 << " vs " << std::endl
1177 << toString(textBuf, actual);
1178 actual = doLineBreak(textBuf, *measured, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
1179 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1180 << " vs " << std::endl
1181 << toString(textBuf, actual);
1182 }
1183 }
1184
TEST_F(OptimalLineBreakerTest,ExtentTest)1185 TEST_F(OptimalLineBreakerTest, ExtentTest) {
1186 constexpr HyphenationFrequency NO_HYPHEN = HyphenationFrequency::None;
1187 const std::vector<uint16_t> textBuf = utf8ToUtf16("The \u3042\u3044\u3046 is Japanese.");
1188
1189 constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
1190 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1191 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1192 {
1193 constexpr float LINE_WIDTH = 1000;
1194 std::vector<LineBreakExpectation> expect = {
1195 {"The \u3042\u3044\u3046 is Japanese.", 200, NO_START_HYPHEN, NO_END_HYPHEN,
1196 CUSTOM_ASCENT, CUSTOM_DESCENT},
1197 };
1198
1199 auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHEN, LINE_WIDTH);
1200 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1201 << " vs " << std::endl
1202 << toString(textBuf, actual);
1203 }
1204 {
1205 constexpr float LINE_WIDTH = 200;
1206 std::vector<LineBreakExpectation> expect = {
1207 {"The \u3042\u3044\u3046 is Japanese.", 200, NO_START_HYPHEN, NO_END_HYPHEN,
1208 CUSTOM_ASCENT, CUSTOM_DESCENT},
1209 };
1210
1211 auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHEN, LINE_WIDTH);
1212 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1213 << " vs " << std::endl
1214 << toString(textBuf, actual);
1215 }
1216 {
1217 constexpr float LINE_WIDTH = 190;
1218 std::vector<LineBreakExpectation> expect = {
1219 {"The \u3042\u3044\u3046 is ", 100, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT,
1220 CUSTOM_DESCENT},
1221 {"Japanese.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1222 };
1223 auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHEN, LINE_WIDTH);
1224 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1225 << " vs " << std::endl
1226 << toString(textBuf, actual);
1227 }
1228 {
1229 constexpr float LINE_WIDTH = 90;
1230 std::vector<LineBreakExpectation> expect = {
1231 {"The \u3042", 50, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1232 {"\u3044\u3046 is ", 50, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT,
1233 CUSTOM_DESCENT},
1234 {"Japanese.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1235 };
1236 auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHEN, LINE_WIDTH);
1237 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1238 << " vs " << std::endl
1239 << toString(textBuf, actual);
1240 }
1241 {
1242 constexpr float LINE_WIDTH = 50;
1243 std::vector<LineBreakExpectation> expect = {
1244 {"The \u3042", 50, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1245 {"\u3044\u3046 is ", 50, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT,
1246 CUSTOM_DESCENT},
1247 {"Japan", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1248 {"ese.", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1249 };
1250 auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHEN, LINE_WIDTH);
1251 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1252 << " vs " << std::endl
1253 << toString(textBuf, actual);
1254 }
1255 {
1256 constexpr float LINE_WIDTH = 40;
1257 std::vector<LineBreakExpectation> expect = {
1258 {"The ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1259 {"\u3042\u3044", 20, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1260 {"\u3046 is ", 40, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1261 {"Japa", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1262 {"nese", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1263 {".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1264 };
1265 auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHEN, 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 = 20;
1272 std::vector<LineBreakExpectation> expect = {
1273 {"T", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1274 {"he ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1275 {"\u3042", 10, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1276 {"\u3044\u3046 ", 20, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT,
1277 CUSTOM_DESCENT},
1278 {"is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1279 {"Ja", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1280 {"pa", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1281 {"ne", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1282 {"se", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1283 {".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1284 };
1285 auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHEN, LINE_WIDTH);
1286 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1287 << " vs " << std::endl
1288 << toString(textBuf, actual);
1289 }
1290 {
1291 constexpr float LINE_WIDTH = 10;
1292 std::vector<LineBreakExpectation> expect = {
1293 {"T", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1294 {"h", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1295 {"e ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1296 {"\u3042", 10, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1297 {"\u3044", 10, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1298 {"\u3046 ", 10, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1299 {"i", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1300 {"s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1301 {"J", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1302 {"a", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1303 {"p", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1304 {"a", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1305 {"n", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1306 {"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1307 {"s", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1308 {"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1309 {".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1310 };
1311 auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHEN, LINE_WIDTH);
1312 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1313 << " vs " << std::endl
1314 << toString(textBuf, actual);
1315 }
1316 }
1317
TEST_F(OptimalLineBreakerTest,testReplacementSpanNotBreakTest_SingleChar)1318 TEST_F(OptimalLineBreakerTest, testReplacementSpanNotBreakTest_SingleChar) {
1319 constexpr float CHAR_WIDTH = 10.0;
1320
1321 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1322 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1323
1324 const auto textBuf = utf8ToUtf16("This is an example \u2639 text.");
1325
1326 // In this test case, assign a replacement run for "U+2639" with 5 times of CHAR_WIDTH.
1327 auto doLineBreak = [=](float width) {
1328 MeasuredTextBuilder builder;
1329 builder.addCustomRun<ConstantRun>(Range(0, 19), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1330 builder.addReplacementRun(19, 21, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1331 builder.addCustomRun<ConstantRun>(Range(21, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
1332 DESCENT);
1333
1334 std::unique_ptr<MeasuredText> measuredText =
1335 builder.build(textBuf, false /* compute hyphenation */,
1336 false /* compute full layout */, nullptr /* no hint */);
1337 RectangleLineWidth rectangleLineWidth(width);
1338 TabStops tabStops(nullptr, 0, 0);
1339 return breakLineOptimal(textBuf, *measuredText, rectangleLineWidth,
1340 BreakStrategy::HighQuality, HyphenationFrequency::None,
1341 false /* justified */);
1342 };
1343
1344 {
1345 constexpr float LINE_WIDTH = 100;
1346 // "is an" is a single replacement span. Do not break.
1347 // clang-format off
1348 std::vector<LineBreakExpectation> expect = {
1349 {"This is an ", 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1350 {"example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1351 {"\u2639 text.", 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1352 };
1353 // clang-format on
1354 const auto actual = doLineBreak(LINE_WIDTH);
1355 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1356 << " vs " << std::endl
1357 << toString(textBuf, actual);
1358 }
1359 {
1360 constexpr float LINE_WIDTH = 90;
1361 // "is an" is a single replacement span. Do not break.
1362 // clang-format off
1363 std::vector<LineBreakExpectation> expect = {
1364 {"This ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1365 {"is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1366 {"example ",70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1367 {"\u2639 ", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1368 {"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1369 };
1370 // clang-format on
1371 const auto actual = doLineBreak(LINE_WIDTH);
1372 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1373 << " vs " << std::endl
1374 << toString(textBuf, actual);
1375 }
1376 {
1377 constexpr float LINE_WIDTH = 10;
1378 // "is an" is a single replacement span. Do not break.
1379 // clang-format off
1380 std::vector<LineBreakExpectation> expect = {
1381 {"T", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1382 {"h", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1383 {"i", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1384 {"s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1385 {"i", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1386 {"s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1387 {"a", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1388 {"n ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1389 {"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1390 {"x", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1391 {"a", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1392 {"m", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1393 {"p", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1394 {"l", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1395 {"e ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1396 {"\u2639 ",50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1397 {"t", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1398 {"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1399 {"x", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1400 {"t", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1401 {".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1402 };
1403 // clang-format on
1404 const auto actual = doLineBreak(LINE_WIDTH);
1405 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1406 << " vs " << std::endl
1407 << toString(textBuf, actual);
1408 }
1409 }
1410
TEST_F(OptimalLineBreakerTest,testReplacementSpanNotBreakTest_MultipleChars)1411 TEST_F(OptimalLineBreakerTest, testReplacementSpanNotBreakTest_MultipleChars) {
1412 constexpr float CHAR_WIDTH = 10.0;
1413
1414 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1415 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1416
1417 const auto textBuf = utf8ToUtf16("This is an example text.");
1418
1419 // In this test case, assign a replacement run for "is an " with 5 times of CHAR_WIDTH.
1420 auto doLineBreak = [=](float width) {
1421 MeasuredTextBuilder builder;
1422 builder.addCustomRun<ConstantRun>(Range(0, 5), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1423 builder.addReplacementRun(5, 11, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1424 builder.addCustomRun<ConstantRun>(Range(11, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
1425 DESCENT);
1426
1427 std::unique_ptr<MeasuredText> measuredText =
1428 builder.build(textBuf, false /* compute hyphenation */,
1429 false /* compute full layout */, nullptr /* no hint */);
1430 RectangleLineWidth rectangleLineWidth(width);
1431 TabStops tabStops(nullptr, 0, 0);
1432 return breakLineOptimal(textBuf, *measuredText, rectangleLineWidth,
1433 BreakStrategy::HighQuality, HyphenationFrequency::None,
1434 false /* justified */);
1435 };
1436
1437 {
1438 constexpr float LINE_WIDTH = 100;
1439 // "is an" is a single replacement span. Do not break.
1440 // clang-format off
1441 std::vector<LineBreakExpectation> expect = {
1442 {"This is an ", 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1443 {"example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1444 {"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1445 };
1446 // clang-format on
1447 const auto actual = doLineBreak(LINE_WIDTH);
1448 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1449 << " vs " << std::endl
1450 << toString(textBuf, actual);
1451 }
1452 {
1453 constexpr float LINE_WIDTH = 90;
1454 // "is an" is a single replacement span. Do not break.
1455 // clang-format off
1456 std::vector<LineBreakExpectation> expect = {
1457 {"This ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1458 {"is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1459 {"example ",70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1460 {"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1461 };
1462 // clang-format on
1463 const auto actual = doLineBreak(LINE_WIDTH);
1464 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1465 << " vs " << std::endl
1466 << toString(textBuf, actual);
1467 }
1468 {
1469 constexpr float LINE_WIDTH = 10;
1470 // "is an" is a single replacement span. Do not break.
1471 // clang-format off
1472 std::vector<LineBreakExpectation> expect = {
1473 {"T", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1474 {"h", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1475 {"i", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1476 {"s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1477 {"is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1478 {"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1479 {"x", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1480 {"a", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1481 {"m", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1482 {"p", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1483 {"l", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1484 {"e ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1485 {"t", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1486 {"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1487 {"x", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1488 {"t", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1489 {".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1490 };
1491 // clang-format on
1492 const auto actual = doLineBreak(LINE_WIDTH);
1493 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1494 << " vs " << std::endl
1495 << toString(textBuf, actual);
1496 }
1497 }
1498
TEST_F(OptimalLineBreakerTest,testReplacementSpanNotBreakTest_continuedReplacementSpan)1499 TEST_F(OptimalLineBreakerTest, testReplacementSpanNotBreakTest_continuedReplacementSpan) {
1500 constexpr float CHAR_WIDTH = 10.0;
1501
1502 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1503 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1504
1505 const auto textBuf = utf8ToUtf16("This is an example text.");
1506
1507 // In this test case, assign a replacement run for "is an " with 5 times of CHAR_WIDTH.
1508 auto doLineBreak = [=](float width) {
1509 MeasuredTextBuilder builder;
1510 builder.addReplacementRun(0, 5, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1511 builder.addReplacementRun(5, 8, 3 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1512 builder.addReplacementRun(8, 11, 3 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1513 builder.addReplacementRun(11, 19, 8 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1514 builder.addReplacementRun(19, 24, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1515 std::unique_ptr<MeasuredText> measuredText =
1516 builder.build(textBuf, false /* compute hyphenation */,
1517 false /* compute full layout */, nullptr /* no hint */);
1518 RectangleLineWidth rectangleLineWidth(width);
1519 TabStops tabStops(nullptr, 0, 0);
1520 return breakLineOptimal(textBuf, *measuredText, rectangleLineWidth,
1521 BreakStrategy::HighQuality, HyphenationFrequency::None,
1522 false /* justified */);
1523 };
1524
1525 {
1526 constexpr float LINE_WIDTH = 100;
1527 // clang-format off
1528 std::vector<LineBreakExpectation> expect = {
1529 {"This ", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1530 {"is an ", 60, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1531 {"example ", 80, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1532 {"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1533 };
1534 // clang-format on
1535 const auto actual = doLineBreak(LINE_WIDTH);
1536 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1537 << " vs " << std::endl
1538 << toString(textBuf, actual);
1539 }
1540 {
1541 constexpr float LINE_WIDTH = 40;
1542 // clang-format off
1543 std::vector<LineBreakExpectation> expect = {
1544 {"This ", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1545 {"is ", 30, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1546 {"an ", 30, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1547 {"example ", 80, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1548 {"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1549 };
1550 // clang-format on
1551 const auto actual = doLineBreak(LINE_WIDTH);
1552 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1553 << " vs " << std::endl
1554 << toString(textBuf, actual);
1555 }
1556 {
1557 constexpr float LINE_WIDTH = 10;
1558 // clang-format off
1559 std::vector<LineBreakExpectation> expect = {
1560 {"This ", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1561 {"is ", 30, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1562 {"an ", 30, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1563 {"example ", 80, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1564 {"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1565 };
1566 // clang-format on
1567 const auto actual = doLineBreak(LINE_WIDTH);
1568 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1569 << " vs " << std::endl
1570 << toString(textBuf, actual);
1571 }
1572 }
1573
TEST_F(OptimalLineBreakerTest,testReplacementSpanNotBreakTest_CJK)1574 TEST_F(OptimalLineBreakerTest, testReplacementSpanNotBreakTest_CJK) {
1575 constexpr float CHAR_WIDTH = 10.0;
1576
1577 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1578 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1579
1580 // Example string: "Today is a sunny day." in Japanese.
1581 const auto textBuf = utf8ToUtf16("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A");
1582
1583 // In this test case, assign a replacement run for "\u6674\u5929" with 5 times of CHAR_WIDTH.
1584 auto doLineBreak = [=](float width) {
1585 MeasuredTextBuilder builder;
1586 builder.addCustomRun<ConstantRun>(Range(0, 3), "ja-JP", CHAR_WIDTH, ASCENT, DESCENT);
1587 builder.addReplacementRun(3, 5, 5 * CHAR_WIDTH, LocaleListCache::getId("ja-JP"));
1588 builder.addCustomRun<ConstantRun>(Range(5, textBuf.size()), "ja-JP", CHAR_WIDTH, ASCENT,
1589 DESCENT);
1590
1591 std::unique_ptr<MeasuredText> measuredText =
1592 builder.build(textBuf, false /* compute hyphenation */,
1593 false /* compute full layout */, nullptr /* no hint */);
1594 RectangleLineWidth rectangleLineWidth(width);
1595 TabStops tabStops(nullptr, 0, 0);
1596 return breakLineOptimal(textBuf, *measuredText, rectangleLineWidth,
1597 BreakStrategy::HighQuality, HyphenationFrequency::None,
1598 false /* justified */);
1599 };
1600
1601 {
1602 constexpr float LINE_WIDTH = 100;
1603 // "\u6674\u5929" is a single replacement span. Do not break.
1604 // clang-format off
1605 std::vector<LineBreakExpectation> expect = {
1606 {"\u672C\u65E5\u306F\u6674\u5929\u306A\u308A",
1607 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1608 };
1609 // clang-format on
1610 const auto actual = doLineBreak(LINE_WIDTH);
1611 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1612 << " vs " << std::endl
1613 << toString(textBuf, actual);
1614 }
1615 {
1616 constexpr float LINE_WIDTH = 90;
1617 // "\u6674\u5929" is a single replacement span. Do not break.
1618 // clang-format off
1619 std::vector<LineBreakExpectation> expect = {
1620 {"\u672C\u65E5\u306F\u6674\u5929\u306A",
1621 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1622 {"\u308A",
1623 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1624 };
1625 // clang-format on
1626 const auto actual = doLineBreak(LINE_WIDTH);
1627 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1628 << " vs " << std::endl
1629 << toString(textBuf, actual);
1630 }
1631 {
1632 constexpr float LINE_WIDTH = 80;
1633 // "\u6674\u5929" is a single replacement span. Do not break.
1634 // clang-format off
1635 std::vector<LineBreakExpectation> expect = {
1636 {"\u672C\u65E5\u306F\u6674\u5929",
1637 80, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1638 {"\u306A\u308A",
1639 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1640 };
1641 // clang-format on
1642 const auto actual = doLineBreak(LINE_WIDTH);
1643 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1644 << " vs " << std::endl
1645 << toString(textBuf, actual);
1646 }
1647 {
1648 constexpr float LINE_WIDTH = 70;
1649 // "\u6674\u5929" is a single replacement span. Do not break.
1650 // clang-format off
1651 std::vector<LineBreakExpectation> expect = {
1652 {"\u672C\u65E5\u306F", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1653 {"\u6674\u5929\u306A\u308A", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1654 };
1655 // clang-format on
1656 const auto actual = doLineBreak(LINE_WIDTH);
1657 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1658 << " vs " << std::endl
1659 << toString(textBuf, actual);
1660 }
1661 {
1662 constexpr float LINE_WIDTH = 60;
1663 // "\u6674\u5929" is a single replacement span. Do not break.
1664 // clang-format off
1665 std::vector<LineBreakExpectation> expect = {
1666 {"\u672C\u65E5\u306F", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1667 {"\u6674\u5929\u306A", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1668 {"\u308A", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1669 };
1670 // clang-format on
1671 const auto actual = doLineBreak(LINE_WIDTH);
1672 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1673 << " vs " << std::endl
1674 << toString(textBuf, actual);
1675 }
1676 {
1677 constexpr float LINE_WIDTH = 50;
1678 // "\u6674\u5929" is a single replacement span. Do not break.
1679 // clang-format off
1680 std::vector<LineBreakExpectation> expect = {
1681 {"\u672C\u65E5\u306F", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1682 {"\u6674\u5929", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1683 {"\u306A\u308A", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1684 };
1685 // clang-format on
1686 const auto actual = doLineBreak(LINE_WIDTH);
1687 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1688 << " vs " << std::endl
1689 << toString(textBuf, actual);
1690 }
1691 {
1692 constexpr float LINE_WIDTH = 40;
1693 // "\u6674\u5929" is a single replacement span. Do not break.
1694 // clang-format off
1695 std::vector<LineBreakExpectation> expect = {
1696 {"\u672C\u65E5\u306F", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1697 {"\u6674\u5929", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1698 {"\u306A\u308A", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1699 };
1700 // clang-format on
1701 const auto actual = doLineBreak(LINE_WIDTH);
1702 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1703 << " vs " << std::endl
1704 << toString(textBuf, actual);
1705 }
1706 {
1707 constexpr float LINE_WIDTH = 10;
1708 // "\u6674\u5929" is a single replacement span. Do not break.
1709 // clang-format off
1710 std::vector<LineBreakExpectation> expect = {
1711 {"\u672C", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1712 {"\u65E5", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1713 {"\u306F", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1714 {"\u6674\u5929", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1715 {"\u306A", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1716 {"\u308A", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1717 };
1718 // clang-format on
1719 const auto actual = doLineBreak(LINE_WIDTH);
1720 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1721 << " vs " << std::endl
1722 << toString(textBuf, actual);
1723 }
1724 }
1725
1726 // http://b/119657685
1727 // Following test case is for verifying that the ReplacementSpan should not be broken into multiple
1728 // pieces. The actual break point is not a part of expectation. For example, it would be good to
1729 // break the starting offset of the ReplacementSpan for some case.
TEST_F(OptimalLineBreakerTest,testReplacementSpan_GraphemeLineBreakWithMultipleRepalcementSpans)1730 TEST_F(OptimalLineBreakerTest, testReplacementSpan_GraphemeLineBreakWithMultipleRepalcementSpans) {
1731 constexpr float CHAR_WIDTH = 10.0;
1732
1733 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1734 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1735
1736 const auto textBuf = utf8ToUtf16("ab de\u00A0\u00A0fg ij\u00A0\u00A0kl no\u00A0\u00A0pq st");
1737
1738 auto doLineBreak = [=](float width) {
1739 MeasuredTextBuilder builder;
1740 builder.addReplacementRun(0, 5, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1741 builder.addCustomRun<ConstantRun>(Range(5, 7), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1742 builder.addReplacementRun(7, 12, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1743 builder.addCustomRun<ConstantRun>(Range(12, 14), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1744 builder.addReplacementRun(14, 19, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1745 builder.addCustomRun<ConstantRun>(Range(19, 21), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1746 builder.addReplacementRun(21, 26, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1747
1748 std::unique_ptr<MeasuredText> measuredText =
1749 builder.build(textBuf, false /* compute hyphenation */,
1750 false /* compute full layout */, nullptr /* no hint */);
1751 RectangleLineWidth rectangleLineWidth(width);
1752 TabStops tabStops(nullptr, 0, 0);
1753 return breakLineOptimal(textBuf, *measuredText, rectangleLineWidth,
1754 BreakStrategy::HighQuality, HyphenationFrequency::None,
1755 false /* justified */);
1756 };
1757
1758 {
1759 constexpr float LINE_WIDTH = 1000;
1760 // clang-format off
1761 std::vector<LineBreakExpectation> expect = {
1762 {"ab de\u00A0\u00A0fg ij\u00A0\u00A0kl no\u00A0\u00A0pq st",
1763 260, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1764 };
1765 // clang-format on
1766 const auto actual = doLineBreak(LINE_WIDTH);
1767 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1768 << " vs " << std::endl
1769 << toString(textBuf, actual);
1770 }
1771 {
1772 constexpr float LINE_WIDTH = 250;
1773 // clang-format off
1774 std::vector<LineBreakExpectation> expect = {
1775 {"ab de\u00A0\u00A0fg ij\u00A0\u00A0kl no\u00A0\u00A0",
1776 210, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1777 {"pq st",
1778 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1779 };
1780 // clang-format on
1781 const auto actual = doLineBreak(LINE_WIDTH);
1782 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1783 << " vs " << std::endl
1784 << toString(textBuf, actual);
1785 }
1786 {
1787 constexpr float LINE_WIDTH = 180;
1788 // clang-format off
1789 std::vector<LineBreakExpectation> expect = {
1790 {"ab de\u00A0\u00A0fg ij\u00A0\u00A0",
1791 140, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1792 {"kl no\u00A0\u00A0pq st",
1793 120, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1794 };
1795 // clang-format on
1796 const auto actual = doLineBreak(LINE_WIDTH);
1797 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1798 << " vs " << std::endl
1799 << toString(textBuf, actual);
1800 }
1801 {
1802 constexpr float LINE_WIDTH = 130;
1803 // clang-format off
1804 std::vector<LineBreakExpectation> expect = {
1805 {"ab de\u00A0\u00A0fg ij\u00A0",
1806 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1807 {"\u00A0kl no\u00A0\u00A0pq st",
1808 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1809 };
1810 // clang-format on
1811 const auto actual = doLineBreak(LINE_WIDTH);
1812 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1813 << " vs " << std::endl
1814 << toString(textBuf, actual);
1815 }
1816 {
1817 constexpr float LINE_WIDTH = 110;
1818 // clang-format off
1819 std::vector<LineBreakExpectation> expect = {
1820 {"ab de\u00A0", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1821 {"\u00A0fg ij\u00A0\u00A0", 80, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1822 {"kl no\u00A0\u00A0", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1823 {"pq st", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1824 };
1825 // clang-format on
1826 const auto actual = doLineBreak(LINE_WIDTH);
1827 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1828 << " vs " << std::endl
1829 << toString(textBuf, actual);
1830 }
1831 {
1832 constexpr float LINE_WIDTH = 60;
1833 // clang-format off
1834 std::vector<LineBreakExpectation> expect = {
1835 {"ab de\u00A0", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1836 {"\u00A0fg ij", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1837 {"\u00A0\u00A0", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1838 {"kl no\u00A0", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1839 {"\u00A0pq st", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1840 };
1841 // clang-format on
1842 const auto actual = doLineBreak(LINE_WIDTH);
1843 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1844 << " vs " << std::endl
1845 << toString(textBuf, actual);
1846 }
1847 {
1848 constexpr float LINE_WIDTH = 50;
1849 // clang-format off
1850 std::vector<LineBreakExpectation> expect = {
1851 {"ab de", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1852 {"\u00A0\u00A0", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1853 {"fg ij", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1854 {"\u00A0\u00A0", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1855 {"kl no", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1856 {"\u00A0\u00A0", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1857 {"pq st", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1858 };
1859 // clang-format on
1860 const auto actual = doLineBreak(LINE_WIDTH);
1861 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1862 << " vs " << std::endl
1863 << toString(textBuf, actual);
1864 }
1865 }
1866
TEST_F(OptimalLineBreakerTest,testReplacementSpanNotBreakTest_with_punctuation)1867 TEST_F(OptimalLineBreakerTest, testReplacementSpanNotBreakTest_with_punctuation) {
1868 constexpr float CHAR_WIDTH = 10.0;
1869
1870 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1871 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1872
1873 const auto textBuf = utf8ToUtf16("This (is an) example text.");
1874
1875 // In this test case, assign a replacement run for "U+2639" with 5 times of CHAR_WIDTH.
1876 auto doLineBreak = [=](float width) {
1877 MeasuredTextBuilder builder;
1878 builder.addCustomRun<ConstantRun>(Range(0, 6), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1879 builder.addReplacementRun(6, 11, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1880 builder.addCustomRun<ConstantRun>(Range(11, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
1881 DESCENT);
1882
1883 std::unique_ptr<MeasuredText> measuredText =
1884 builder.build(textBuf, false /* compute hyphenation */,
1885 false /* compute full layout */, nullptr /* no hint */);
1886 RectangleLineWidth rectangleLineWidth(width);
1887 TabStops tabStops(nullptr, 0, 0);
1888 return breakLineOptimal(textBuf, *measuredText, rectangleLineWidth,
1889 BreakStrategy::HighQuality, HyphenationFrequency::Normal,
1890 false /* justified */);
1891 };
1892
1893 {
1894 constexpr float LINE_WIDTH = 1000;
1895 // "is an" is a single replacement span. Do not break.
1896 // clang-format off
1897 std::vector<LineBreakExpectation> expect = {
1898 {"This (is an) example text.",
1899 260, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1900 };
1901 // clang-format on
1902 const auto actual = doLineBreak(LINE_WIDTH);
1903 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1904 << " vs " << std::endl
1905 << toString(textBuf, actual);
1906 }
1907 {
1908 constexpr float LINE_WIDTH = 250;
1909 // "is an" is a single replacement span. Do not break.
1910 // clang-format off
1911 std::vector<LineBreakExpectation> expect = {
1912 {"This (is an) example ", 200, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1913 {"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1914 };
1915 // clang-format on
1916 const auto actual = doLineBreak(LINE_WIDTH);
1917 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1918 << " vs " << std::endl
1919 << toString(textBuf, actual);
1920 }
1921 {
1922 constexpr float LINE_WIDTH = 190;
1923 // "is an" is a single replacement span. Do not break.
1924 // clang-format off
1925 std::vector<LineBreakExpectation> expect = {
1926 {"This (is an) ", 120, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1927 {"example text.", 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1928 };
1929 // clang-format on
1930 const auto actual = doLineBreak(LINE_WIDTH);
1931 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1932 << " vs " << std::endl
1933 << toString(textBuf, actual);
1934 }
1935 {
1936 constexpr float LINE_WIDTH = 120;
1937 // "is an" is a single replacement span. Do not break.
1938 // clang-format off
1939 std::vector<LineBreakExpectation> expect = {
1940 {"This (is an) ", 120, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1941 {"example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1942 {"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1943 };
1944 // clang-format on
1945 const auto actual = doLineBreak(LINE_WIDTH);
1946 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1947 << " vs " << std::endl
1948 << toString(textBuf, actual);
1949 }
1950 {
1951 constexpr float LINE_WIDTH = 110;
1952 // "is an" is a single replacement span. Do not break.
1953 // clang-format off
1954 std::vector<LineBreakExpectation> expect = {
1955 {"This ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1956 {"(is an) ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1957 {"example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1958 {"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1959 };
1960 // clang-format on
1961 const auto actual = doLineBreak(LINE_WIDTH);
1962 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1963 << " vs " << std::endl
1964 << toString(textBuf, actual);
1965 }
1966 {
1967 constexpr float LINE_WIDTH = 60;
1968 // "is an" is a single replacement span. Do not break.
1969 // clang-format off
1970 std::vector<LineBreakExpectation> expect = {
1971 {"This ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1972 {"(is an", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1973 {") ex", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1974 {"ample ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1975 {"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1976 };
1977 // clang-format on
1978 const auto actual = doLineBreak(LINE_WIDTH);
1979 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1980 << " vs " << std::endl
1981 << toString(textBuf, actual);
1982 }
1983 {
1984 constexpr float LINE_WIDTH = 50;
1985 // "is an" is a single replacement span. Do not break.
1986 // clang-format off
1987 std::vector<LineBreakExpectation> expect = {
1988 {"This ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1989 {"(", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1990 {"is an", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1991 {") ex", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1992 {"ample ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1993 {"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1994 };
1995 // clang-format on
1996 const auto actual = doLineBreak(LINE_WIDTH);
1997 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1998 << " vs " << std::endl
1999 << toString(textBuf, actual);
2000 }
2001 {
2002 constexpr float LINE_WIDTH = 40;
2003 // "is an" is a single replacement span. Do not break.
2004 // clang-format off
2005 std::vector<LineBreakExpectation> expect = {
2006 {"This ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2007 // TODO(nona): This might be wrongly broken. "(is an" should be broken into "(" and
2008 // "is an" as the desperate break.
2009 {"(is an", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2010 {") ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2011 {"exa", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2012 {"mple ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2013 {"text", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2014 {".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2015 };
2016 // clang-format on
2017 const auto actual = doLineBreak(LINE_WIDTH);
2018 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2019 << " vs " << std::endl
2020 << toString(textBuf, actual);
2021 }
2022 {
2023 constexpr float LINE_WIDTH = 10;
2024 // "is an" is a single replacement span. Do not break.
2025 // clang-format off
2026 std::vector<LineBreakExpectation> expect = {
2027 {"T", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2028 {"h", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2029 {"i", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2030 {"s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2031 // TODO(nona): This might be wrongly broken. "(is an" should be broken into "(" and
2032 // "is an" as the desperate break.
2033 {"(is an", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2034 {") ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2035 {"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2036 {"x", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2037 {"a", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2038 {"m", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2039 {"p", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2040 {"l", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2041 {"e ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2042 {"t", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2043 {"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2044 {"x", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2045 {"t", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2046 {".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2047 };
2048 // clang-format on
2049 const auto actual = doLineBreak(LINE_WIDTH);
2050 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2051 << " vs " << std::endl
2052 << toString(textBuf, actual);
2053 }
2054 }
2055
TEST_F(OptimalLineBreakerTest,testControllCharAfterSpace)2056 TEST_F(OptimalLineBreakerTest, testControllCharAfterSpace) {
2057 constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
2058 constexpr BreakStrategy BALANCED = BreakStrategy::Balanced;
2059 constexpr HyphenationFrequency NO_HYPHENATION = HyphenationFrequency::None;
2060 const std::vector<uint16_t> textBuf = utf8ToUtf16("example \u2066example");
2061
2062 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
2063 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
2064 {
2065 constexpr float LINE_WIDTH = 90;
2066 // Note that HarfBuzz assigns 0px for control characters regardless of glyph existence in
2067 // the font.
2068 std::vector<LineBreakExpectation> expect = {
2069 {"example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2070 {"\u2066example", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2071 };
2072
2073 auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
2074 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2075 << " vs " << std::endl
2076 << toString(textBuf, actual);
2077 actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
2078 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2079 << " vs " << std::endl
2080 << toString(textBuf, actual);
2081 }
2082 }
2083
TEST_F(OptimalLineBreakerTest,roundingError)2084 TEST_F(OptimalLineBreakerTest, roundingError) {
2085 MeasuredTextBuilder builder;
2086 auto family1 = buildFontFamily("Ascii.ttf");
2087 std::vector<std::shared_ptr<FontFamily>> families = {family1};
2088 auto fc = std::make_shared<FontCollection>(families);
2089 MinikinPaint paint(fc);
2090 paint.size = 56.0f; // Make 1em=56px
2091 paint.scaleX = 1;
2092 paint.letterSpacing = -0.093f;
2093 paint.localeListId = LocaleListCache::getId("en-US");
2094 const std::vector<uint16_t> textBuffer = utf8ToUtf16("8888888888888888888");
2095
2096 float measured = Layout::measureText(textBuffer, Range(0, textBuffer.size()), Bidi::LTR, paint,
2097 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, nullptr);
2098
2099 builder.addStyleRun(0, textBuffer.size(), std::move(paint), false);
2100 std::unique_ptr<MeasuredText> measuredText =
2101 builder.build(textBuffer, false /* compute hyphenation */,
2102 false /* compute full layout */, nullptr /* no hint */);
2103 RectangleLineWidth rectangleLineWidth(measured);
2104 TabStops tabStops(nullptr, 0, 10);
2105 LineBreakResult r = doLineBreak(textBuffer, *measuredText, BreakStrategy::Balanced,
2106 HyphenationFrequency::None, measured);
2107
2108 EXPECT_EQ(1u, r.breakPoints.size());
2109 }
2110
2111 } // namespace
2112 } // namespace minikin
2113