xref: /aosp_15_r20/frameworks/minikin/tests/unittest/GreedyLineBreakerTest.cpp (revision 834a2baab5fdfc28e9a428ee87c7ea8f6a06a53d)
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <com_android_text_flags.h>
18 #include <flag_macros.h>
19 #include <gtest/gtest.h>
20 
21 #include <memory>
22 
23 #include "FileUtils.h"
24 #include "FontTestUtils.h"
25 #include "GreedyLineBreaker.h"
26 #include "HyphenatorMap.h"
27 #include "LineBreakerTestHelper.h"
28 #include "LocaleListCache.h"
29 #include "MinikinInternal.h"
30 #include "UnicodeUtils.h"
31 #include "WordBreaker.h"
32 #include "minikin/Hyphenator.h"
33 
34 namespace minikin {
35 namespace {
36 
37 using line_breaker_test_helper::ConstantRun;
38 using line_breaker_test_helper::LineBreakExpectation;
39 using line_breaker_test_helper::RectangleLineWidth;
40 using line_breaker_test_helper::sameLineBreak;
41 using line_breaker_test_helper::toString;
42 
43 // The ascent/descent of Ascii.ttf with text size = 10.
44 constexpr float ASCENT = -80.0f;
45 constexpr float DESCENT = 20.0f;
46 
47 // The ascent/descent of CustomExtent.ttf with text size = 10.
48 constexpr float CUSTOM_ASCENT = -160.0f;
49 constexpr float CUSTOM_DESCENT = 40.0f;
50 
51 // A test string for Japanese. The meaning is that "Today is a sunny day."
52 // The expected line break of phrase and non-phrase cases are:
53 //     Phrase: | \u672C\u65E5\u306F | \u6674\u5929\u306A\u308A\u3002 |
54 // Non-Phrase: | \u672C | \u65E5 | \u306F | \u6674 | \u5929 | \u306A | \u308A\u3002 |
55 const char* JP_TEXT = "\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002";
56 
57 // A test string for Korean. The meaning is that "I want to eat breakfast."
58 // The phrase based line break breaks at spaces, non-phrase based line break breaks at grapheme.
59 const char* KO_TEXT =
60         "\uC544\uCE68\uBC25\uC744\u0020\uBA39\uACE0\u0020\uC2F6\uC2B5\uB2C8\uB2E4\u002E";
61 
62 class GreedyLineBreakerTest : public testing::Test {
63 public:
GreedyLineBreakerTest()64     GreedyLineBreakerTest() {}
65 
~GreedyLineBreakerTest()66     virtual ~GreedyLineBreakerTest() {}
67 
SetUp()68     virtual void SetUp() override {
69         mHyphenationPattern = readWholeFile("/system/usr/hyphen-data/hyph-en-us.hyb");
70         Hyphenator* hyphenator =
71                 Hyphenator::loadBinary(mHyphenationPattern.data(), mHyphenationPattern.size(),
72                                        2 /* min prefix */, 2 /* min suffix */, "en-US");
73         HyphenatorMap::add("en-US", hyphenator);
74         HyphenatorMap::add("pl", Hyphenator::loadBinary(nullptr, 0, 0, 0, "pl"));
75     }
76 
TearDown()77     virtual void TearDown() override { HyphenatorMap::clear(); }
78 
79 protected:
doLineBreak(const U16StringPiece & textBuffer,bool doHyphenation,float lineWidth)80     LineBreakResult doLineBreak(const U16StringPiece& textBuffer, bool doHyphenation,
81                                 float lineWidth) {
82         return doLineBreak(textBuffer, doHyphenation, "en-US", lineWidth);
83     }
84 
doLineBreakForJapanese(const U16StringPiece & textBuffer,LineBreakWordStyle lbwStyle,const std::string & lang,float lineWidth)85     LineBreakResult doLineBreakForJapanese(const U16StringPiece& textBuffer,
86                                            LineBreakWordStyle lbwStyle, const std::string& lang,
87                                            float lineWidth) {
88         MeasuredTextBuilder builder;
89         auto family = buildFontFamily("Japanese.ttf");
90         std::vector<std::shared_ptr<FontFamily>> families = {family};
91         auto fc = FontCollection::create(families);
92         MinikinPaint paint(fc);
93         paint.size = 10.0f;  // Make 1em=10px
94         paint.localeListId = LocaleListCache::getId(lang);
95         builder.addStyleRun(0, textBuffer.size(), std::move(paint), (int)LineBreakStyle::None,
96                             (int)lbwStyle, true, false);
97         std::unique_ptr<MeasuredText> measuredText = builder.build(
98                 textBuffer, false /* compute hyphenation */, false /* compute full layout */,
99                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
100         RectangleLineWidth rectangleLineWidth(lineWidth);
101         TabStops tabStops(nullptr, 0, 10);
102         return breakLineGreedy(textBuffer, *measuredText, rectangleLineWidth, tabStops, false,
103                                false);
104     }
105 
doLineBreakForKorean(const U16StringPiece & textBuffer,LineBreakWordStyle lbwStyle,const std::string & lang,float lineWidth)106     LineBreakResult doLineBreakForKorean(const U16StringPiece& textBuffer,
107                                          LineBreakWordStyle lbwStyle, const std::string& lang,
108                                          float lineWidth) {
109         MeasuredTextBuilder builder;
110         auto family = buildFontFamily("Hangul.ttf");
111         std::vector<std::shared_ptr<FontFamily>> families = {family};
112         auto fc = FontCollection::create(families);
113         MinikinPaint paint(fc);
114         paint.size = 10.0f;  // Make 1em=10px
115         paint.localeListId = LocaleListCache::getId(lang);
116         builder.addStyleRun(0, textBuffer.size(), std::move(paint), (int)LineBreakStyle::None,
117                             (int)lbwStyle, true, false);
118         std::unique_ptr<MeasuredText> measuredText = builder.build(
119                 textBuffer, false /* compute hyphenation */, false /* compute full layout */,
120                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
121         RectangleLineWidth rectangleLineWidth(lineWidth);
122         TabStops tabStops(nullptr, 0, 10);
123         return breakLineGreedy(textBuffer, *measuredText, rectangleLineWidth, tabStops, false,
124                                false);
125     }
126 
doLineBreak(const U16StringPiece & textBuffer,bool doHyphenation,const std::string & lang,float lineWidth)127     LineBreakResult doLineBreak(const U16StringPiece& textBuffer, bool doHyphenation,
128                                 const std::string& lang, float lineWidth) {
129         MeasuredTextBuilder builder;
130         auto family1 = buildFontFamily("Ascii.ttf");
131         auto family2 = buildFontFamily("CustomExtent.ttf");
132         std::vector<std::shared_ptr<FontFamily>> families = {family1, family2};
133         auto fc = FontCollection::create(families);
134         MinikinPaint paint(fc);
135         paint.size = 10.0f;  // Make 1em=10px
136         paint.localeListId = LocaleListCache::getId(lang);
137         builder.addStyleRun(0, textBuffer.size(), std::move(paint), (int)LineBreakStyle::None,
138                             (int)LineBreakWordStyle::None, true, false);
139         std::unique_ptr<MeasuredText> measuredText = builder.build(
140                 textBuffer, false /* compute hyphenation */, false /* compute full layout */,
141                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
142         RectangleLineWidth rectangleLineWidth(lineWidth);
143         TabStops tabStops(nullptr, 0, 10);
144         return breakLineGreedy(textBuffer, *measuredText, rectangleLineWidth, tabStops,
145                                doHyphenation, false);
146     }
147 
doLineBreakWithNoHyphenSpan(const U16StringPiece & textBuffer,const Range & noHyphenRange,float lineWidth)148     LineBreakResult doLineBreakWithNoHyphenSpan(const U16StringPiece& textBuffer,
149                                                 const Range& noHyphenRange, float lineWidth) {
150         MeasuredTextBuilder builder;
151         auto family1 = buildFontFamily("Ascii.ttf");
152         auto family2 = buildFontFamily("CustomExtent.ttf");
153         std::vector<std::shared_ptr<FontFamily>> families = {family1, family2};
154         auto fc = FontCollection::create(families);
155         if (noHyphenRange.getStart() != 0) {
156             MinikinPaint paint(fc);
157             paint.size = 10.0f;  // Make 1em=10px
158             paint.localeListId = LocaleListCache::getId("en-US");
159             builder.addStyleRun(0, noHyphenRange.getStart(), std::move(paint),
160                                 (int)LineBreakStyle::None, (int)LineBreakWordStyle::None,
161                                 true /* hyphenation */, false);
162         }
163         MinikinPaint paint(fc);
164         paint.size = 10.0f;  // Make 1em=10px
165         paint.localeListId = LocaleListCache::getId("en-US");
166         builder.addStyleRun(noHyphenRange.getStart(), noHyphenRange.getEnd(), std::move(paint),
167                             (int)LineBreakStyle::None, (int)LineBreakWordStyle::None,
168                             false /* hyphenation */, false);
169         if (noHyphenRange.getEnd() != textBuffer.size()) {
170             MinikinPaint paint(fc);
171             paint.size = 10.0f;  // Make 1em=10px
172             paint.localeListId = LocaleListCache::getId("en-US");
173             builder.addStyleRun(noHyphenRange.getEnd(), textBuffer.size(), std::move(paint),
174                                 (int)LineBreakStyle::None, (int)LineBreakWordStyle::None,
175                                 true /* hyphenation */, false);
176         }
177 
178         std::unique_ptr<MeasuredText> measuredText = builder.build(
179                 textBuffer, false /* compute hyphenation */, false /* compute full layout */,
180                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
181         RectangleLineWidth rectangleLineWidth(lineWidth);
182         TabStops tabStops(nullptr, 0, 10);
183         return breakLineGreedy(textBuffer, *measuredText, rectangleLineWidth, tabStops,
184                                true /* doHyphenation */, false);
185     }
186 
doLineBreakForBounds(const U16StringPiece & textBuffer,bool doHyphenation,float lineWidth)187     LineBreakResult doLineBreakForBounds(const U16StringPiece& textBuffer, bool doHyphenation,
188                                          float lineWidth) {
189         MeasuredTextBuilder builder;
190         auto family1 = buildFontFamily("OvershootTest.ttf");
191         auto family2 = buildFontFamily("Ascii.ttf");
192         std::vector<std::shared_ptr<FontFamily>> families = {family1, family2};
193         auto fc = FontCollection::create(families);
194         MinikinPaint paint(fc);
195         paint.size = 10.0f;  // Make 1em=10px
196         paint.localeListId = LocaleListCache::getId("en-US");
197         builder.addStyleRun(0, textBuffer.size(), std::move(paint), (int)LineBreakStyle::None,
198                             (int)LineBreakWordStyle::None, true, false);
199         std::unique_ptr<MeasuredText> measuredText = builder.build(
200                 textBuffer, false /* compute hyphenation */, false /* compute full layout */,
201                 true /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
202         RectangleLineWidth rectangleLineWidth(lineWidth);
203         TabStops tabStops(nullptr, 0, 10);
204         return breakLineGreedy(textBuffer, *measuredText, rectangleLineWidth, tabStops,
205                                doHyphenation, true);
206     }
207 
doLineBreakForLetterSpacing(const U16StringPiece & textBuffer,float letterSpacing,float lineWidth)208     LineBreakResult doLineBreakForLetterSpacing(const U16StringPiece& textBuffer,
209                                                 float letterSpacing, float lineWidth) {
210         MeasuredTextBuilder builder;
211         auto family1 = buildFontFamily("Ascii.ttf");
212         auto family2 = buildFontFamily("CustomExtent.ttf");
213         std::vector<std::shared_ptr<FontFamily>> families = {family1, family2};
214         auto fc = FontCollection::create(families);
215         MinikinPaint paint(fc);
216         paint.size = 10.0f;  // Make 1em=10px
217         paint.letterSpacing = letterSpacing;
218         paint.scaleX = 1.0f;
219         paint.localeListId = LocaleListCache::getId("en-US");
220         builder.addStyleRun(0, textBuffer.size(), std::move(paint), (int)LineBreakStyle::None,
221                             (int)LineBreakWordStyle::None, true, false);
222         std::unique_ptr<MeasuredText> measuredText = builder.build(
223                 textBuffer, false /* compute hyphenation */, false /* compute full layout */,
224                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
225         RectangleLineWidth rectangleLineWidth(lineWidth);
226         TabStops tabStops(nullptr, 0, 10);
227         return breakLineGreedy(textBuffer, *measuredText, rectangleLineWidth, tabStops,
228                                false /* hyphenation */, false);
229     }
230 
231 private:
232     std::vector<uint8_t> mHyphenationPattern;
233 };
234 
TEST_F(GreedyLineBreakerTest,roundingError)235 TEST_F(GreedyLineBreakerTest, roundingError) {
236     MeasuredTextBuilder builder;
237     auto family1 = buildFontFamily("Ascii.ttf");
238     std::vector<std::shared_ptr<FontFamily>> families = {family1};
239     auto fc = FontCollection::create(families);
240     MinikinPaint paint(fc);
241     paint.size = 56.0f;  // Make 1em=56px
242     paint.scaleX = 1;
243     paint.letterSpacing = -0.093f;
244     paint.localeListId = LocaleListCache::getId("en-US");
245     const std::vector<uint16_t> textBuffer = utf8ToUtf16("8888888888888888888");
246 
247     float measured = Layout::measureText(textBuffer, Range(0, textBuffer.size()), Bidi::LTR, paint,
248                                          StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, nullptr,
249                                          nullptr, nullptr, RunFlag::WHOLE_LINE);
250 
251     builder.addStyleRun(0, textBuffer.size(), std::move(paint), (int)LineBreakStyle::None,
252                         (int)LineBreakWordStyle::None, true, false);
253     std::unique_ptr<MeasuredText> measuredText = builder.build(
254             textBuffer, false /* compute hyphenation */, false /* compute full layout */,
255             false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
256     RectangleLineWidth rectangleLineWidth(measured);
257     TabStops tabStops(nullptr, 0, 10);
258     LineBreakResult r = breakLineGreedy(textBuffer, *measuredText, rectangleLineWidth, tabStops,
259                                         false /* do hyphenation */, false /* useBoundsForWidth */);
260 
261     EXPECT_EQ(1u, r.breakPoints.size());
262 }
263 
TEST_F(GreedyLineBreakerTest,testBreakWithoutHyphenation)264 TEST_F(GreedyLineBreakerTest, testBreakWithoutHyphenation) {
265     constexpr bool NO_HYPHEN = false;  // No hyphenation in this test case.
266     const std::vector<uint16_t> textBuf = utf8ToUtf16("This is an example text.");
267 
268     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
269     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
270     // Note that disable clang-format everywhere since aligned expectation is more readable.
271     {
272         constexpr float LINE_WIDTH = 1000;
273         std::vector<LineBreakExpectation> expect = {
274                 {"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
275         };
276 
277         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
278         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
279                                                    << " vs " << std::endl
280                                                    << toString(textBuf, actual);
281     }
282     {
283         constexpr float LINE_WIDTH = 240;
284         std::vector<LineBreakExpectation> expect = {
285                 {"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
286         };
287 
288         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
289         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
290                                                    << " vs " << std::endl
291                                                    << toString(textBuf, actual);
292     }
293     {
294         constexpr float LINE_WIDTH = 230;
295         // clang-format off
296         std::vector<LineBreakExpectation> expect = {
297                 { "This is an example ", 180, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
298                 { "text."              ,  50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
299         };
300         // clang-format on
301 
302         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
303         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
304                                                    << " vs " << std::endl
305                                                    << toString(textBuf, actual);
306     }
307     {
308         constexpr float LINE_WIDTH = 80;
309         // clang-format off
310         std::vector<LineBreakExpectation> expect = {
311                 { "This is ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
312                 { "an "     , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
313                 { "example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
314                 { "text."   , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
315         };
316         // clang-format on
317 
318         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
319         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
320                                                    << " vs " << std::endl
321                                                    << toString(textBuf, actual);
322     }
323     {
324         constexpr float LINE_WIDTH = 70;
325         // clang-format off
326         std::vector<LineBreakExpectation> expect = {
327                 { "This is ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
328                 { "an "     , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
329                 { "example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
330                 { "text."   , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
331         };
332         // clang-format on
333 
334         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
335         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
336                                                    << " vs " << std::endl
337                                                    << toString(textBuf, actual);
338     }
339     {
340         constexpr float LINE_WIDTH = 60;
341         // clang-format off
342         std::vector<LineBreakExpectation> expect = {
343                 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
344                 { "is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
345                 { "exampl", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
346                 { "e "    , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
347                 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
348         };
349         // clang-format on
350 
351         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
352         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
353                                                    << " vs " << std::endl
354                                                    << toString(textBuf, actual);
355     }
356     {
357         constexpr float LINE_WIDTH = 50;
358         // clang-format off
359         std::vector<LineBreakExpectation> expect = {
360                 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
361                 { "is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
362                 { "examp" , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
363                 { "le "   , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
364                 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
365         };
366         // clang-format on
367 
368         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
369         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
370                                                    << " vs " << std::endl
371                                                    << toString(textBuf, actual);
372     }
373     {
374         constexpr float LINE_WIDTH = 40;
375         // clang-format off
376         std::vector<LineBreakExpectation> expect = {
377                 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
378                 { "is "   , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
379                 { "an "   , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
380                 { "exam"  , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
381                 { "ple "  , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
382                 { "text"  , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
383                 { "."     , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
384         };
385         // clang-format on
386 
387         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
388         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
389                                                    << " vs " << std::endl
390                                                    << toString(textBuf, actual);
391     }
392     {
393         constexpr float LINE_WIDTH = 30;
394         // clang-format off
395         std::vector<LineBreakExpectation> expect = {
396                 { "Thi" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
397                 { "s "  , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
398                 { "is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
399                 { "an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
400                 { "exa" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
401                 { "mpl" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
402                 { "e "  , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
403                 { "tex" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
404                 { "t."  , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
405         };
406         // clang-format on
407 
408         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
409         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
410                                                    << " vs " << std::endl
411                                                    << toString(textBuf, actual);
412     }
413     {
414         constexpr float LINE_WIDTH = 20;
415         // clang-format off
416         std::vector<LineBreakExpectation> expect = {
417                 { "Th" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
418                 { "is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
419                 { "is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
420                 { "an ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
421                 { "ex" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
422                 { "am" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
423                 { "pl" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
424                 { "e " , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
425                 { "te" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
426                 { "xt" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
427                 { "."  , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
428         };
429         // clang-format on
430 
431         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
432         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
433                                                    << " vs " << std::endl
434                                                    << toString(textBuf, actual);
435     }
436     {
437         constexpr float LINE_WIDTH = 10;
438         // clang-format off
439         std::vector<LineBreakExpectation> expect = {
440                 { "T" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
441                 { "h" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
442                 { "i" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
443                 { "s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
444                 { "i" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
445                 { "s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
446                 { "a" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
447                 { "n ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
448                 { "e" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
449                 { "x" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
450                 { "a" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
451                 { "m" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
452                 { "p" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
453                 { "l" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
454                 { "e ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
455                 { "t" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
456                 { "e" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
457                 { "x" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
458                 { "t" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
459                 { "." , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
460         };
461         // clang-format on
462 
463         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
464         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
465                                                    << " vs " << std::endl
466                                                    << toString(textBuf, actual);
467     }
468 }
469 
TEST_F(GreedyLineBreakerTest,testBreakWithHyphenation)470 TEST_F(GreedyLineBreakerTest, testBreakWithHyphenation) {
471     constexpr bool NO_HYPHEN = true;  // Do hyphenation in this test case.
472     // "hyphenation" is hyphnated to "hy-phen-a-tion".
473     const std::vector<uint16_t> textBuf = utf8ToUtf16("Hyphenation is hyphenation.");
474 
475     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
476     constexpr EndHyphenEdit END_HYPHEN = EndHyphenEdit::INSERT_HYPHEN;
477     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
478 
479     // Note that disable clang-format everywhere since aligned expectation is more readable.
480     {
481         constexpr float LINE_WIDTH = 1000;
482         std::vector<LineBreakExpectation> expect = {
483                 {"Hyphenation is hyphenation.", 270, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT,
484                  DESCENT},
485         };
486 
487         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
488         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
489                                                    << " vs " << std::endl
490                                                    << toString(textBuf, actual);
491     }
492     {
493         constexpr float LINE_WIDTH = 270;
494         std::vector<LineBreakExpectation> expect = {
495                 {"Hyphenation is hyphenation.", 270, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT,
496                  DESCENT},
497         };
498 
499         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
500         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
501                                                    << " vs " << std::endl
502                                                    << toString(textBuf, actual);
503     }
504     {
505         constexpr float LINE_WIDTH = 260;
506         // clang-format off
507         std::vector<LineBreakExpectation> expect = {
508                 { "Hyphenation is " , 140, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
509                 { "hyphenation."    , 120, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
510         };
511         // clang-format on
512 
513         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
514         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
515                                                    << " vs " << std::endl
516                                                    << toString(textBuf, actual);
517     }
518     {
519         constexpr float LINE_WIDTH = 170;
520         // clang-format off
521         std::vector<LineBreakExpectation> expect = {
522                 { "Hyphenation is " , 140, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
523                 { "hyphenation."    , 120, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
524         };
525         // clang-format on
526 
527         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
528         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
529                                                    << " vs " << std::endl
530                                                    << toString(textBuf, actual);
531     }
532     {
533         constexpr float LINE_WIDTH = 120;
534         // clang-format off
535         std::vector<LineBreakExpectation> expect = {
536                 { "Hyphenation " , 110, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
537                 { "is "          , 20 , NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
538                 { "hyphenation." , 120, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
539         };
540         // clang-format on
541 
542         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
543         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
544                                                    << " vs " << std::endl
545                                                    << toString(textBuf, actual);
546     }
547     {
548         constexpr float LINE_WIDTH = 100;
549         // clang-format off
550         std::vector<LineBreakExpectation> expect = {
551                 { "Hyphena-", 80, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
552                 { "tion is ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
553                 { "hyphena-", 80, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
554                 { "tion."   , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
555         };
556         // clang-format on
557 
558         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
559         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
560                                                    << " vs " << std::endl
561                                                    << toString(textBuf, actual);
562     }
563     {
564         constexpr float LINE_WIDTH = 80;
565         // clang-format off
566         std::vector<LineBreakExpectation> expect = {
567                 { "Hyphena-", 80, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
568                 { "tion is ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
569                 { "hyphena-", 80, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
570                 { "tion."   , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
571         };
572         // clang-format on
573 
574         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
575         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
576                                                    << " vs " << std::endl
577                                                    << toString(textBuf, actual);
578     }
579     {
580         constexpr float LINE_WIDTH = 70;
581         // clang-format off
582         std::vector<LineBreakExpectation> expect = {
583                 { "Hyphen-", 70, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
584                 { "ation " , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
585                 { "is "    , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
586                 { "hyphen-", 70, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
587                 { "ation." , 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
588         };
589         // clang-format on
590 
591         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
592         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
593                                                    << " vs " << std::endl
594                                                    << toString(textBuf, actual);
595     }
596     {
597         constexpr float LINE_WIDTH = 60;
598         // clang-format off
599         std::vector<LineBreakExpectation> expect = {
600                 { "Hy-"   , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
601                 { "phena-", 60, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
602                 { "tion " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
603                 { "is "   , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
604                 { "hy-"   , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
605                 { "phena-", 60, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
606                 { "tion." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
607         };
608         // clang-format on
609 
610         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
611         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
612                                                    << " vs " << std::endl
613                                                    << toString(textBuf, actual);
614     }
615     {
616         constexpr float LINE_WIDTH = 50;
617         // clang-format off
618         std::vector<LineBreakExpectation> expect = {
619                 { "Hy-"   , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
620                 { "phen-" , 50, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
621                 { "ation ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
622                 { "is "   , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
623                 { "hy-"   , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
624                 { "phen-" , 50, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
625                 { "a-"    , 20, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
626                 { "tion." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
627         };
628         // clang-format on
629 
630         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
631         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
632                                                    << " vs " << std::endl
633                                                    << toString(textBuf, actual);
634     }
635     {
636         constexpr float LINE_WIDTH = 40;
637         // clang-format off
638         std::vector<LineBreakExpectation> expect = {
639                 { "Hy-"  , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
640                 { "phen" , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
641                 { "a-"   , 20, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
642                 { "tion ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
643                 { "is "  , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
644                 { "hy-"  , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
645                 { "phen" , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
646                 { "a-"   , 20, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
647                 { "tion" , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
648                 { "."    , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
649         };
650         // clang-format on
651 
652         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
653         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
654                                                    << " vs " << std::endl
655                                                    << toString(textBuf, actual);
656     }
657     {
658         constexpr float LINE_WIDTH = 30;
659         // clang-format off
660         std::vector<LineBreakExpectation> expect = {
661                 { "Hy-", 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
662                 { "phe", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
663                 { "na-", 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
664                 { "tio", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
665                 { "n " , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
666                 { "is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
667                 { "hy-", 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
668                 { "phe", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
669                 { "na-", 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
670                 { "tio", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
671                 { "n." , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
672         };
673         // clang-format on
674 
675         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
676         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
677                                                    << " vs " << std::endl
678                                                    << toString(textBuf, actual);
679     }
680     {
681         constexpr float LINE_WIDTH = 20;
682         // clang-format off
683         std::vector<LineBreakExpectation> expect = {
684                 { "Hy" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
685                 { "ph" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
686                 { "en" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
687                 { "a-" , 20, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
688                 { "ti" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
689                 { "on ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
690                 { "is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
691                 { "hy" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
692                 { "ph" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
693                 { "en" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
694                 { "a-" , 20, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
695                 { "ti" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
696                 { "on" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
697                 { "."  , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
698         };
699         // clang-format on
700 
701         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
702         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
703                                                    << " vs " << std::endl
704                                                    << toString(textBuf, actual);
705     }
706     {
707         constexpr float LINE_WIDTH = 10;
708         // clang-format off
709         std::vector<LineBreakExpectation> expect = {
710                 { "H" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
711                 { "y" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
712                 { "p" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
713                 { "h" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
714                 { "e" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
715                 { "n" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
716                 { "a" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
717                 { "t" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
718                 { "i" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
719                 { "o" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
720                 { "n ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
721                 { "i" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
722                 { "s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
723                 { "h" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
724                 { "y" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
725                 { "p" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
726                 { "h" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
727                 { "e" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
728                 { "n" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
729                 { "a" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
730                 { "t" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
731                 { "i" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
732                 { "o" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
733                 { "n" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
734                 { "." , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
735         };
736         // clang-format on
737 
738         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
739         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
740                                                    << " vs " << std::endl
741                                                    << toString(textBuf, actual);
742     }
743 }
744 
TEST_F(GreedyLineBreakerTest,testHyphenationStartLineChange)745 TEST_F(GreedyLineBreakerTest, testHyphenationStartLineChange) {
746     constexpr bool DO_HYPHEN = true;  // Do hyphenation in this test case.
747     // "hyphenation" is hyphnated to "hy-phen-a-tion".
748     const std::vector<uint16_t> textBuf = utf8ToUtf16("czerwono-niebieska");
749 
750     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
751     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
752     constexpr StartHyphenEdit START_HYPHEN = StartHyphenEdit::INSERT_HYPHEN;
753 
754     // Note that disable clang-format everywhere since aligned expectation is more readable.
755     {
756         constexpr float LINE_WIDTH = 1000;
757         std::vector<LineBreakExpectation> expect = {
758                 {"czerwono-niebieska", 180, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
759         };
760 
761         const auto actual = doLineBreak(textBuf, DO_HYPHEN, "pl", LINE_WIDTH);
762         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
763                                                    << " vs " << std::endl
764                                                    << toString(textBuf, actual);
765     }
766     {
767         constexpr float LINE_WIDTH = 180;
768         std::vector<LineBreakExpectation> expect = {
769                 {"czerwono-niebieska", 180, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
770         };
771 
772         const auto actual = doLineBreak(textBuf, DO_HYPHEN, "pl", LINE_WIDTH);
773         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
774                                                    << " vs " << std::endl
775                                                    << toString(textBuf, actual);
776     }
777     {
778         constexpr float LINE_WIDTH = 130;
779         // clang-format off
780         std::vector<LineBreakExpectation> expect = {
781                 {"czerwono-" ,  90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
782                 {"-niebieska", 100,    START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
783         };
784         // clang-format on
785 
786         const auto actual = doLineBreak(textBuf, DO_HYPHEN, "pl", LINE_WIDTH);
787         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
788                                                    << " vs " << std::endl
789                                                    << toString(textBuf, actual);
790     }
791 }
792 
TEST_F(GreedyLineBreakerTest,testZeroWidthLine)793 TEST_F(GreedyLineBreakerTest, testZeroWidthLine) {
794     constexpr bool DO_HYPHEN = true;  // Do hyphenation in this test case.
795     constexpr float LINE_WIDTH = 0;
796 
797     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
798     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
799 
800     {
801         const auto textBuf = utf8ToUtf16("");
802         std::vector<LineBreakExpectation> expect = {};
803         const auto actual = doLineBreak(textBuf, DO_HYPHEN, LINE_WIDTH);
804         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
805                                                    << " vs " << std::endl
806                                                    << toString(textBuf, actual);
807     }
808     {
809         const auto textBuf = utf8ToUtf16("A");
810         std::vector<LineBreakExpectation> expect = {
811                 {"A", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
812         };
813         const auto actual = doLineBreak(textBuf, DO_HYPHEN, LINE_WIDTH);
814         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
815                                                    << " vs " << std::endl
816                                                    << toString(textBuf, actual);
817     }
818     {
819         const auto textBuf = utf8ToUtf16("AB");
820         std::vector<LineBreakExpectation> expect = {
821                 {"A", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
822                 {"B", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
823         };
824         const auto actual = doLineBreak(textBuf, DO_HYPHEN, LINE_WIDTH);
825         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
826                                                    << " vs " << std::endl
827                                                    << toString(textBuf, actual);
828     }
829 }
830 
TEST_F(GreedyLineBreakerTest,testZeroWidthCharacter)831 TEST_F(GreedyLineBreakerTest, testZeroWidthCharacter) {
832     constexpr float CHAR_WIDTH = 0.0;
833     constexpr bool DO_HYPHEN = true;  // Do hyphenation in this test case.
834 
835     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
836     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
837     {
838         constexpr float LINE_WIDTH = 1.0;
839         const auto textBuf = utf8ToUtf16("This is an example text.");
840         std::vector<LineBreakExpectation> expect = {
841                 {"This is an example text.", 0, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
842         };
843 
844         MeasuredTextBuilder builder;
845         builder.addCustomRun<ConstantRun>(Range(0, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
846                                           DESCENT);
847         std::unique_ptr<MeasuredText> measuredText = builder.build(
848                 textBuf, false /* compute hyphenation */, false /* compute full layout */,
849                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
850         RectangleLineWidth rectangleLineWidth(LINE_WIDTH);
851         TabStops tabStops(nullptr, 0, 10);
852         const auto actual = breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops,
853                                             DO_HYPHEN, false /* useBoundsForWidth */);
854         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
855                                                    << " vs " << std::endl
856                                                    << toString(textBuf, actual);
857     }
858     {
859         constexpr float LINE_WIDTH = 0.0;
860         const auto textBuf = utf8ToUtf16("This is an example text.");
861         std::vector<LineBreakExpectation> expect = {
862                 {"This is an example text.", 0, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
863         };
864         MeasuredTextBuilder builder;
865         builder.addCustomRun<ConstantRun>(Range(0, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
866                                           DESCENT);
867         std::unique_ptr<MeasuredText> measuredText = builder.build(
868                 textBuf, false /* compute hyphenation */, false /* compute full layout */,
869                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
870         RectangleLineWidth rectangleLineWidth(LINE_WIDTH);
871         TabStops tabStops(nullptr, 0, 10);
872         const auto actual = breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops,
873                                             DO_HYPHEN, false /* useBoundsForWidth */);
874         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
875                                                    << " vs " << std::endl
876                                                    << toString(textBuf, actual);
877     }
878 }
879 
TEST_F(GreedyLineBreakerTest,testLocaleSwitchTest)880 TEST_F(GreedyLineBreakerTest, testLocaleSwitchTest) {
881     constexpr float CHAR_WIDTH = 10.0;
882     constexpr bool DO_HYPHEN = true;  // Do hyphenation in this test case.
883 
884     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
885     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
886 
887     constexpr float LINE_WIDTH = 240;
888     const auto textBuf = utf8ToUtf16("This is an example text.");
889     {
890         std::vector<LineBreakExpectation> expect = {
891                 {"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
892         };
893 
894         MeasuredTextBuilder builder;
895         builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
896         builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
897                                           DESCENT);
898         std::unique_ptr<MeasuredText> measuredText = builder.build(
899                 textBuf, false /* compute hyphenation */, false /* compute full layout */,
900                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
901         RectangleLineWidth rectangleLineWidth(LINE_WIDTH);
902         TabStops tabStops(nullptr, 0, 0);
903 
904         const auto actual = breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops,
905                                             DO_HYPHEN, false /* useBoundsForWidth */);
906         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
907                                                    << " vs " << std::endl
908                                                    << toString(textBuf, actual);
909     }
910     {
911         std::vector<LineBreakExpectation> expect = {
912                 {"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
913         };
914 
915         MeasuredTextBuilder builder;
916         builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
917         builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "fr-FR", CHAR_WIDTH, ASCENT,
918                                           DESCENT);
919         std::unique_ptr<MeasuredText> measuredText = builder.build(
920                 textBuf, false /* compute hyphenation */, false /* compute full layout */,
921                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
922         RectangleLineWidth rectangleLineWidth(LINE_WIDTH);
923         TabStops tabStops(nullptr, 0, 0);
924 
925         const auto actual = breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops,
926                                             DO_HYPHEN, false /* useBoundsForWidth */);
927         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
928                                                    << " vs " << std::endl
929                                                    << toString(textBuf, actual);
930     }
931 }
932 
TEST_F(GreedyLineBreakerTest,testEmailOrUrl)933 TEST_F(GreedyLineBreakerTest, testEmailOrUrl) {
934     constexpr bool DO_HYPHEN = true;  // Do hyphenation in this test case.
935 
936     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
937     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
938     {
939         constexpr float LINE_WIDTH = 240;
940         const auto textBuf = utf8ToUtf16("This is an url: http://a.b");
941         std::vector<LineBreakExpectation> expect = {
942                 {"This is an url: ", 150, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
943                 {"http://a.b", 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
944         };
945         const auto actual = doLineBreak(textBuf, DO_HYPHEN, LINE_WIDTH);
946         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
947                                                    << " vs " << std::endl
948                                                    << toString(textBuf, actual);
949     }
950     {
951         constexpr float LINE_WIDTH = 240;
952         const auto textBuf = utf8ToUtf16("This is an email: [email protected]");
953         std::vector<LineBreakExpectation> expect = {
954                 {"This is an email: ", 170, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
955                 {"[email protected]", 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
956         };
957         const auto actual = doLineBreak(textBuf, DO_HYPHEN, LINE_WIDTH);
958         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
959                                                    << " vs " << std::endl
960                                                    << toString(textBuf, actual);
961     }
962 }
963 
TEST_F(GreedyLineBreakerTest,testLocaleSwitch_InEmailOrUrl)964 TEST_F(GreedyLineBreakerTest, testLocaleSwitch_InEmailOrUrl) {
965     constexpr float CHAR_WIDTH = 10.0;
966     constexpr bool DO_HYPHEN = true;  // Do hyphenation in this test case.
967 
968     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
969     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
970 
971     constexpr float LINE_WIDTH = 240;
972     {
973         const auto textBuf = utf8ToUtf16("This is an url: http://a.b");
974         std::vector<LineBreakExpectation> expect = {
975                 {"This is an url: ", 150, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
976                 {"http://a.b", 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
977         };
978 
979         MeasuredTextBuilder builder;
980         builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
981         builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "fr-FR", CHAR_WIDTH, ASCENT,
982                                           DESCENT);
983         std::unique_ptr<MeasuredText> measuredText = builder.build(
984                 textBuf, false /* compute hyphenation */, false /* compute full layout */,
985                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
986         RectangleLineWidth rectangleLineWidth(LINE_WIDTH);
987         TabStops tabStops(nullptr, 0, 0);
988 
989         const auto actual = breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops,
990                                             DO_HYPHEN, false /* useBoundsForWidth */);
991         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
992                                                    << " vs " << std::endl
993                                                    << toString(textBuf, actual);
994     }
995     {
996         const auto textBuf = utf8ToUtf16("This is an email: [email protected]");
997         std::vector<LineBreakExpectation> expect = {
998                 {"This is an email: ", 170, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
999                 {"[email protected]", 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1000         };
1001 
1002         MeasuredTextBuilder builder;
1003         builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1004         builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "fr-FR", CHAR_WIDTH, ASCENT,
1005                                           DESCENT);
1006         std::unique_ptr<MeasuredText> measuredText = builder.build(
1007                 textBuf, false /* compute hyphenation */, false /* compute full layout */,
1008                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
1009         RectangleLineWidth rectangleLineWidth(LINE_WIDTH);
1010         TabStops tabStops(nullptr, 0, 0);
1011 
1012         const auto actual = breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops,
1013                                             DO_HYPHEN, false /* useBoundsForWidth */);
1014         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1015                                                    << " vs " << std::endl
1016                                                    << toString(textBuf, actual);
1017     }
1018 }
1019 
1020 // b/68669534
TEST_F(GreedyLineBreakerTest,CrashFix_Space_Tab)1021 TEST_F(GreedyLineBreakerTest, CrashFix_Space_Tab) {
1022     constexpr float CHAR_WIDTH = 10.0;
1023     constexpr bool DO_HYPHEN = true;  // Do hyphenation in this test case.
1024 
1025     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1026     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1027     {
1028         constexpr float LINE_WIDTH = 50;
1029         const auto textBuf = utf8ToUtf16("a \tb");
1030         std::vector<LineBreakExpectation> expect = {
1031                 {"a \tb", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1032         };
1033 
1034         MeasuredTextBuilder builder;
1035         builder.addCustomRun<ConstantRun>(Range(0, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
1036                                           DESCENT);
1037         std::unique_ptr<MeasuredText> measuredText = builder.build(
1038                 textBuf, false /* compute hyphenation */, false /* compute full layout */,
1039                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
1040         RectangleLineWidth rectangleLineWidth(LINE_WIDTH);
1041         TabStops tabStops(nullptr, 0, CHAR_WIDTH);
1042 
1043         const auto actual = breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops,
1044                                             DO_HYPHEN, false /* useBoundsForWidth */);
1045         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1046                                                    << " vs " << std::endl
1047                                                    << toString(textBuf, actual);
1048     }
1049 }
1050 
TEST_F(GreedyLineBreakerTest,ExtentTest)1051 TEST_F(GreedyLineBreakerTest, ExtentTest) {
1052     constexpr bool NO_HYPHEN = false;  // No hyphenation in this test case.
1053     const std::vector<uint16_t> textBuf = utf8ToUtf16("The \u3042\u3044\u3046 is Japanese.");
1054 
1055     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1056     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1057     {
1058         constexpr float LINE_WIDTH = 1000;
1059         std::vector<LineBreakExpectation> expect = {
1060                 {"The \u3042\u3044\u3046 is Japanese.", 200, NO_START_HYPHEN, NO_END_HYPHEN,
1061                  CUSTOM_ASCENT, CUSTOM_DESCENT},
1062         };
1063 
1064         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
1065         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1066                                                    << " vs " << std::endl
1067                                                    << toString(textBuf, actual);
1068     }
1069     {
1070         constexpr float LINE_WIDTH = 200;
1071         std::vector<LineBreakExpectation> expect = {
1072                 {"The \u3042\u3044\u3046 is Japanese.", 200, NO_START_HYPHEN, NO_END_HYPHEN,
1073                  CUSTOM_ASCENT, CUSTOM_DESCENT},
1074         };
1075 
1076         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
1077         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1078                                                    << " vs " << std::endl
1079                                                    << toString(textBuf, actual);
1080     }
1081     {
1082         constexpr float LINE_WIDTH = 190;
1083         std::vector<LineBreakExpectation> expect = {
1084                 {"The \u3042\u3044\u3046 is ", 100, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT,
1085                  CUSTOM_DESCENT},
1086                 {"Japanese.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1087         };
1088 
1089         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
1090         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1091                                                    << " vs " << std::endl
1092                                                    << toString(textBuf, actual);
1093     }
1094     {
1095         constexpr float LINE_WIDTH = 90;
1096         std::vector<LineBreakExpectation> expect = {
1097                 {"The \u3042\u3044\u3046 ", 70, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT,
1098                  CUSTOM_DESCENT},
1099                 {"is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1100                 {"Japanese.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1101         };
1102 
1103         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
1104         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1105                                                    << " vs " << std::endl
1106                                                    << toString(textBuf, actual);
1107     }
1108     {
1109         constexpr float LINE_WIDTH = 50;
1110         std::vector<LineBreakExpectation> expect = {
1111                 {"The \u3042", 50, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1112                 {"\u3044\u3046 is ", 50, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT,
1113                  CUSTOM_DESCENT},
1114                 {"Japan", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1115                 {"ese.", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1116         };
1117 
1118         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
1119         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1120                                                    << " vs " << std::endl
1121                                                    << toString(textBuf, actual);
1122     }
1123     {
1124         constexpr float LINE_WIDTH = 40;
1125         std::vector<LineBreakExpectation> expect = {
1126                 {"The ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1127                 {"\u3042\u3044\u3046 ", 30, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT,
1128                  CUSTOM_DESCENT},
1129                 {"is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1130                 {"Japa", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1131                 {"nese", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1132                 {".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1133         };
1134 
1135         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
1136         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1137                                                    << " vs " << std::endl
1138                                                    << toString(textBuf, actual);
1139     }
1140     {
1141         constexpr float LINE_WIDTH = 20;
1142         std::vector<LineBreakExpectation> expect = {
1143                 {"Th", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1144                 {"e ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1145                 {"\u3042\u3044", 20, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1146                 {"\u3046 ", 10, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1147                 {"is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1148                 {"Ja", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1149                 {"pa", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1150                 {"ne", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1151                 {"se", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1152                 {".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1153         };
1154 
1155         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
1156         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1157                                                    << " vs " << std::endl
1158                                                    << toString(textBuf, actual);
1159     }
1160     {
1161         constexpr float LINE_WIDTH = 10;
1162         std::vector<LineBreakExpectation> expect = {
1163                 {"T", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1164                 {"h", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1165                 {"e ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1166                 {"\u3042", 10, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1167                 {"\u3044", 10, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1168                 {"\u3046 ", 10, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1169                 {"i", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1170                 {"s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1171                 {"J", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1172                 {"a", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1173                 {"p", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1174                 {"a", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1175                 {"n", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1176                 {"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1177                 {"s", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1178                 {"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1179                 {".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1180         };
1181 
1182         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
1183         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1184                                                    << " vs " << std::endl
1185                                                    << toString(textBuf, actual);
1186     }
1187 }
1188 
TEST_F(GreedyLineBreakerTest,testReplacementSpanNotBreakTest_SingleChar)1189 TEST_F(GreedyLineBreakerTest, testReplacementSpanNotBreakTest_SingleChar) {
1190     constexpr float CHAR_WIDTH = 10.0;
1191     constexpr bool DO_HYPHEN = true;  // Do hyphenation in this test case.
1192 
1193     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1194     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1195 
1196     const auto textBuf = utf8ToUtf16("This is an example \u2639 text.");
1197 
1198     // In this test case, assign a replacement run for "U+2639" with 5 times of CHAR_WIDTH.
1199     auto doLineBreak = [=](float width) {
1200         MeasuredTextBuilder builder;
1201         builder.addCustomRun<ConstantRun>(Range(0, 19), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1202         builder.addReplacementRun(19, 20, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1203         builder.addCustomRun<ConstantRun>(Range(20, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
1204                                           DESCENT);
1205 
1206         std::unique_ptr<MeasuredText> measuredText = builder.build(
1207                 textBuf, false /* compute hyphenation */, false /* compute full layout */,
1208                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
1209         RectangleLineWidth rectangleLineWidth(width);
1210         TabStops tabStops(nullptr, 0, 0);
1211         return breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN,
1212                                false /* useBoundsForWidth */);
1213     };
1214 
1215     {
1216         constexpr float LINE_WIDTH = 100;
1217         // "is an" is a single replacement span. Do not break.
1218         // clang-format off
1219         std::vector<LineBreakExpectation> expect = {
1220                 {"This is an ",   100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1221                 {"example ",       70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1222                 {"\u2639 ",        50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1223                 {"text.",          50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1224         };
1225         // clang-format on
1226         const auto actual = doLineBreak(LINE_WIDTH);
1227         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1228                                                    << " vs " << std::endl
1229                                                    << toString(textBuf, actual);
1230     }
1231     {
1232         constexpr float LINE_WIDTH = 90;
1233         // "is an" is a single replacement span. Do not break.
1234         // clang-format off
1235         std::vector<LineBreakExpectation> expect = {
1236                 {"This is ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1237                 {"an ",      20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1238                 {"example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1239                 {"\u2639 ",  50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1240                 {"text.",    50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1241         };
1242         // clang-format on
1243         const auto actual = doLineBreak(LINE_WIDTH);
1244         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1245                                                    << " vs " << std::endl
1246                                                    << toString(textBuf, actual);
1247     }
1248     {
1249         constexpr float LINE_WIDTH = 10;
1250         // "is an" is a single replacement span. Do not break.
1251         // clang-format off
1252         std::vector<LineBreakExpectation> expect = {
1253                 {"T",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1254                 {"h",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1255                 {"i",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1256                 {"s ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1257                 {"i",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1258                 {"s ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1259                 {"a",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1260                 {"n ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1261                 {"e",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1262                 {"x",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1263                 {"a",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1264                 {"m",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1265                 {"p",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1266                 {"l",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1267                 {"e ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1268                 // TODO: This should be "\u2639 " since should not count the trailing line end space
1269                 {"\u2639", 50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1270                 {" ",       0, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1271                 {"t",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1272                 {"e",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1273                 {"x",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1274                 {"t",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1275                 {".",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1276         };
1277         // clang-format on
1278         const auto actual = doLineBreak(LINE_WIDTH);
1279         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1280                                                    << " vs " << std::endl
1281                                                    << toString(textBuf, actual);
1282     }
1283 }
1284 
TEST_F(GreedyLineBreakerTest,testReplacementSpanNotBreakTest_MultipleChars)1285 TEST_F(GreedyLineBreakerTest, testReplacementSpanNotBreakTest_MultipleChars) {
1286     constexpr float CHAR_WIDTH = 10.0;
1287     constexpr bool DO_HYPHEN = true;  // Do hyphenation in this test case.
1288 
1289     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1290     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1291 
1292     const auto textBuf = utf8ToUtf16("This is an example text.");
1293 
1294     // In this test case, assign a replacement run for "is an " with 5 times of CHAR_WIDTH.
1295     auto doLineBreak = [=](float width) {
1296         MeasuredTextBuilder builder;
1297         builder.addCustomRun<ConstantRun>(Range(0, 5), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1298         builder.addReplacementRun(5, 11, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1299         builder.addCustomRun<ConstantRun>(Range(11, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
1300                                           DESCENT);
1301 
1302         std::unique_ptr<MeasuredText> measuredText = builder.build(
1303                 textBuf, false /* compute hyphenation */, false /* compute full layout */,
1304                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
1305         RectangleLineWidth rectangleLineWidth(width);
1306         TabStops tabStops(nullptr, 0, 0);
1307         return breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN,
1308                                false /* useBoundsForWidth */);
1309     };
1310 
1311     {
1312         constexpr float LINE_WIDTH = 100;
1313         // "is an" is a single replacement span. Do not break.
1314         // clang-format off
1315         std::vector<LineBreakExpectation> expect = {
1316                 {"This is an ",   100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1317                 {"example ",       70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1318                 {"text.",          50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1319         };
1320         // clang-format on
1321         const auto actual = doLineBreak(LINE_WIDTH);
1322         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1323                                                    << " vs " << std::endl
1324                                                    << toString(textBuf, actual);
1325     }
1326     {
1327         constexpr float LINE_WIDTH = 90;
1328         // "is an" is a single replacement span. Do not break.
1329         // clang-format off
1330         std::vector<LineBreakExpectation> expect = {
1331                 {"This ",   40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1332                 {"is an ",  50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1333                 {"example ",70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1334                 {"text.",   50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1335         };
1336         // clang-format on
1337         const auto actual = doLineBreak(LINE_WIDTH);
1338         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1339                                                    << " vs " << std::endl
1340                                                    << toString(textBuf, actual);
1341     }
1342     {
1343         constexpr float LINE_WIDTH = 10;
1344         // "is an" is a single replacement span. Do not break.
1345         // clang-format off
1346         std::vector<LineBreakExpectation> expect = {
1347                 {"T",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1348                 {"h",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1349                 {"i",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1350                 {"s ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1351                 {"is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1352                 {"e",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1353                 {"x",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1354                 {"a",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1355                 {"m",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1356                 {"p",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1357                 {"l",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1358                 {"e ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1359                 {"t",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1360                 {"e",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1361                 {"x",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1362                 {"t",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1363                 {".",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1364         };
1365         // clang-format on
1366         const auto actual = doLineBreak(LINE_WIDTH);
1367         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1368                                                    << " vs " << std::endl
1369                                                    << toString(textBuf, actual);
1370     }
1371 }
1372 
TEST_F(GreedyLineBreakerTest,testReplacementSpanNotBreakTest_CJK)1373 TEST_F(GreedyLineBreakerTest, testReplacementSpanNotBreakTest_CJK) {
1374     constexpr float CHAR_WIDTH = 10.0;
1375     constexpr bool DO_HYPHEN = true;  // Do hyphenation in this test case.
1376 
1377     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1378     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1379 
1380     // Example string: "Today is a sunny day." in Japanese.
1381     const auto textBuf = utf8ToUtf16("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A");
1382 
1383     // In this test case, assign a replacement run for "\u6674\u5929" with 5 times of CHAR_WIDTH.
1384     auto doLineBreak = [=](float width) {
1385         MeasuredTextBuilder builder;
1386         builder.addCustomRun<ConstantRun>(Range(0, 3), "ja-JP", CHAR_WIDTH, ASCENT, DESCENT);
1387         builder.addReplacementRun(3, 5, 5 * CHAR_WIDTH, LocaleListCache::getId("ja-JP"));
1388         builder.addCustomRun<ConstantRun>(Range(5, textBuf.size()), "ja-JP", CHAR_WIDTH, ASCENT,
1389                                           DESCENT);
1390 
1391         std::unique_ptr<MeasuredText> measuredText = builder.build(
1392                 textBuf, false /* compute hyphenation */, false /* compute full layout */,
1393                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
1394         RectangleLineWidth rectangleLineWidth(width);
1395         TabStops tabStops(nullptr, 0, 0);
1396         return breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN,
1397                                false /* useBoundsForWidth */);
1398     };
1399 
1400     {
1401         constexpr float LINE_WIDTH = 100;
1402         // "\u6674\u5929" is a single replacement span. Do not break.
1403         // clang-format off
1404         std::vector<LineBreakExpectation> expect = {
1405                 {"\u672C\u65E5\u306F\u6674\u5929\u306A\u308A",
1406                   100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1407         };
1408         // clang-format on
1409         const auto actual = doLineBreak(LINE_WIDTH);
1410         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1411                                                    << " vs " << std::endl
1412                                                    << toString(textBuf, actual);
1413     }
1414     {
1415         constexpr float LINE_WIDTH = 90;
1416         // "\u6674\u5929" is a single replacement span. Do not break.
1417         // clang-format off
1418         std::vector<LineBreakExpectation> expect = {
1419                 {"\u672C\u65E5\u306F\u6674\u5929\u306A",
1420                   90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1421                 {"\u308A",
1422                   10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1423         };
1424         // clang-format on
1425         const auto actual = doLineBreak(LINE_WIDTH);
1426         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1427                                                    << " vs " << std::endl
1428                                                    << toString(textBuf, actual);
1429     }
1430     {
1431         constexpr float LINE_WIDTH = 80;
1432         // "\u6674\u5929" is a single replacement span. Do not break.
1433         // clang-format off
1434         std::vector<LineBreakExpectation> expect = {
1435                 {"\u672C\u65E5\u306F\u6674\u5929",
1436                   80, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1437                 {"\u306A\u308A",
1438                   20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1439         };
1440         // clang-format on
1441         const auto actual = doLineBreak(LINE_WIDTH);
1442         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1443                                                    << " vs " << std::endl
1444                                                    << toString(textBuf, actual);
1445     }
1446     {
1447         constexpr float LINE_WIDTH = 70;
1448         // "\u6674\u5929" is a single replacement span. Do not break.
1449         // clang-format off
1450         std::vector<LineBreakExpectation> expect = {
1451                 {"\u672C\u65E5\u306F", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1452                 {"\u6674\u5929\u306A\u308A", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1453         };
1454         // clang-format on
1455         const auto actual = doLineBreak(LINE_WIDTH);
1456         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1457                                                    << " vs " << std::endl
1458                                                    << toString(textBuf, actual);
1459     }
1460     {
1461         constexpr float LINE_WIDTH = 60;
1462         // "\u6674\u5929" is a single replacement span. Do not break.
1463         // clang-format off
1464         std::vector<LineBreakExpectation> expect = {
1465                 {"\u672C\u65E5\u306F", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1466                 {"\u6674\u5929\u306A", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1467                 {"\u308A",             10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1468         };
1469         // clang-format on
1470         const auto actual = doLineBreak(LINE_WIDTH);
1471         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1472                                                    << " vs " << std::endl
1473                                                    << toString(textBuf, actual);
1474     }
1475     {
1476         constexpr float LINE_WIDTH = 50;
1477         // "\u6674\u5929" is a single replacement span. Do not break.
1478         // clang-format off
1479         std::vector<LineBreakExpectation> expect = {
1480                 {"\u672C\u65E5\u306F", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1481                 {"\u6674\u5929",       50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1482                 {"\u306A\u308A",       20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1483         };
1484         // clang-format on
1485         const auto actual = doLineBreak(LINE_WIDTH);
1486         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1487                                                    << " vs " << std::endl
1488                                                    << toString(textBuf, actual);
1489     }
1490     {
1491         constexpr float LINE_WIDTH = 40;
1492         // "\u6674\u5929" is a single replacement span. Do not break.
1493         // clang-format off
1494         std::vector<LineBreakExpectation> expect = {
1495                 {"\u672C\u65E5\u306F", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1496                 {"\u6674\u5929",       50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1497                 {"\u306A\u308A",       20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1498         };
1499         // clang-format on
1500         const auto actual = doLineBreak(LINE_WIDTH);
1501         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1502                                                    << " vs " << std::endl
1503                                                    << toString(textBuf, actual);
1504     }
1505     {
1506         constexpr float LINE_WIDTH = 10;
1507         // "\u6674\u5929" is a single replacement span. Do not break.
1508         // clang-format off
1509         std::vector<LineBreakExpectation> expect = {
1510                 {"\u672C",       10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1511                 {"\u65E5",       10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1512                 {"\u306F",       10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1513                 {"\u6674\u5929", 50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1514                 {"\u306A",       10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1515                 {"\u308A",       10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1516         };
1517         // clang-format on
1518         const auto actual = doLineBreak(LINE_WIDTH);
1519         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1520                                                    << " vs " << std::endl
1521                                                    << toString(textBuf, actual);
1522     }
1523 }
1524 
TEST_F(GreedyLineBreakerTest,testReplacementSpanNotBreakTest_with_punctuation)1525 TEST_F(GreedyLineBreakerTest, testReplacementSpanNotBreakTest_with_punctuation) {
1526     constexpr float CHAR_WIDTH = 10.0;
1527     constexpr bool DO_HYPHEN = true;  // Do hyphenation in this test case.
1528 
1529     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1530     constexpr EndHyphenEdit END_HYPHEN = EndHyphenEdit::INSERT_HYPHEN;
1531     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1532 
1533     const auto textBuf = utf8ToUtf16("This (is an) example text.");
1534 
1535     // In this test case, assign a replacement run for "U+2639" with 5 times of CHAR_WIDTH.
1536     auto doLineBreak = [=](float width) {
1537         MeasuredTextBuilder builder;
1538         builder.addCustomRun<ConstantRun>(Range(0, 6), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1539         builder.addReplacementRun(6, 11, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1540         builder.addCustomRun<ConstantRun>(Range(11, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
1541                                           DESCENT);
1542 
1543         std::unique_ptr<MeasuredText> measuredText = builder.build(
1544                 textBuf, false /* compute hyphenation */, false /* compute full layout */,
1545                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
1546         RectangleLineWidth rectangleLineWidth(width);
1547         TabStops tabStops(nullptr, 0, 0);
1548         return breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN,
1549                                false /* useBoundsForWidth */);
1550     };
1551 
1552     {
1553         constexpr float LINE_WIDTH = 1000;
1554         // "is an" is a single replacement span. Do not break.
1555         // clang-format off
1556         std::vector<LineBreakExpectation> expect = {
1557                 {"This (is an) example text.",
1558                   260, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1559         };
1560         // clang-format on
1561         const auto actual = doLineBreak(LINE_WIDTH);
1562         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1563                                                    << " vs " << std::endl
1564                                                    << toString(textBuf, actual);
1565     }
1566     {
1567         constexpr float LINE_WIDTH = 250;
1568         // "is an" is a single replacement span. Do not break.
1569         // clang-format off
1570         std::vector<LineBreakExpectation> expect = {
1571                 {"This (is an) example ", 200, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1572                 {"text.",                  50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1573         };
1574         // clang-format on
1575         const auto actual = doLineBreak(LINE_WIDTH);
1576         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1577                                                    << " vs " << std::endl
1578                                                    << toString(textBuf, actual);
1579     }
1580     {
1581         constexpr float LINE_WIDTH = 190;
1582         // "is an" is a single replacement span. Do not break.
1583         // clang-format off
1584         std::vector<LineBreakExpectation> expect = {
1585                 {"This (is an) ", 120, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1586                 {"example text.", 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1587         };
1588         // clang-format on
1589         const auto actual = doLineBreak(LINE_WIDTH);
1590         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1591                                                    << " vs " << std::endl
1592                                                    << toString(textBuf, actual);
1593     }
1594     {
1595         constexpr float LINE_WIDTH = 120;
1596         // "is an" is a single replacement span. Do not break.
1597         // clang-format off
1598         std::vector<LineBreakExpectation> expect = {
1599                 {"This (is an) ", 120, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1600                 {"example ",       70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1601                 {"text.",          50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1602         };
1603         // clang-format on
1604         const auto actual = doLineBreak(LINE_WIDTH);
1605         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1606                                                    << " vs " << std::endl
1607                                                    << toString(textBuf, actual);
1608     }
1609     {
1610         constexpr float LINE_WIDTH = 110;
1611         // "is an" is a single replacement span. Do not break.
1612         // clang-format off
1613         std::vector<LineBreakExpectation> expect = {
1614                 {"This ",    40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1615                 {"(is an) ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1616                 {"example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1617                 {"text.",    50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1618         };
1619         // clang-format on
1620         const auto actual = doLineBreak(LINE_WIDTH);
1621         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1622                                                    << " vs " << std::endl
1623                                                    << toString(textBuf, actual);
1624     }
1625     {
1626         constexpr float LINE_WIDTH = 60;
1627         // "is an" is a single replacement span. Do not break.
1628         // clang-format off
1629         std::vector<LineBreakExpectation> expect = {
1630                 {"This ",  40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1631                 {"(is an", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1632                 {") ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1633                 {"exam-",  50, NO_START_HYPHEN,    END_HYPHEN, ASCENT, DESCENT},
1634                 {"ple ",   30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1635                 {"text.",  50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1636         };
1637         // clang-format on
1638         const auto actual = doLineBreak(LINE_WIDTH);
1639         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1640                                                    << " vs " << std::endl
1641                                                    << toString(textBuf, actual);
1642     }
1643     {
1644         constexpr float LINE_WIDTH = 50;
1645         // "is an" is a single replacement span. Do not break.
1646         // clang-format off
1647         std::vector<LineBreakExpectation> expect = {
1648                 {"This ",  40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1649                 {"(",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1650                 {"is an",  50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1651                 {") ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1652                 {"exam-",  50, NO_START_HYPHEN,    END_HYPHEN, ASCENT, DESCENT},
1653                 {"ple ",   30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1654                 {"text.",  50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1655         };
1656         // clang-format on
1657         const auto actual = doLineBreak(LINE_WIDTH);
1658         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1659                                                    << " vs " << std::endl
1660                                                    << toString(textBuf, actual);
1661     }
1662     {
1663         constexpr float LINE_WIDTH = 40;
1664         // "is an" is a single replacement span. Do not break.
1665         // clang-format off
1666         std::vector<LineBreakExpectation> expect = {
1667                 {"This ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1668                 {"(",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1669                 {"is an", 50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1670                 {") ",    10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1671                 {"ex-",   30, NO_START_HYPHEN,    END_HYPHEN, ASCENT, DESCENT},
1672                 {"am-",   30, NO_START_HYPHEN,    END_HYPHEN, ASCENT, DESCENT},
1673                 {"ple ",  30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1674                 {"text",  40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1675                 {".",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1676         };
1677         // clang-format on
1678         const auto actual = doLineBreak(LINE_WIDTH);
1679         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1680                                                    << " vs " << std::endl
1681                                                    << toString(textBuf, actual);
1682     }
1683     {
1684         constexpr float LINE_WIDTH = 10;
1685         // "is an" is a single replacement span. Do not break.
1686         // clang-format off
1687         std::vector<LineBreakExpectation> expect = {
1688                 {"T",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1689                 {"h",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1690                 {"i",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1691                 {"s ",    10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1692                 {"(",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1693                 {"is an", 50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1694                 {") ",    10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1695                 {"e",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1696                 {"x",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1697                 {"a",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1698                 {"m",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1699                 {"p",    10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1700                 {"l",    10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1701                 {"e ",    10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1702                 {"t",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1703                 {"e",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1704                 {"x",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1705                 {"t",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1706                 {".",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1707         };
1708         // clang-format on
1709         const auto actual = doLineBreak(LINE_WIDTH);
1710         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1711                                                    << " vs " << std::endl
1712                                                    << toString(textBuf, actual);
1713     }
1714 }
1715 
TEST_F(GreedyLineBreakerTest,testControllCharAfterSpace)1716 TEST_F(GreedyLineBreakerTest, testControllCharAfterSpace) {
1717     constexpr bool NO_HYPHEN = false;  // No hyphenation in this test case.
1718     const std::vector<uint16_t> textBuf = utf8ToUtf16("example \u2066example");
1719 
1720     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1721     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1722     {
1723         constexpr float LINE_WIDTH = 90;
1724         std::vector<LineBreakExpectation> expect = {
1725                 {"example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1726                 {"\u2066example", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1727         };
1728 
1729         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
1730         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1731                                                    << " vs " << std::endl
1732                                                    << toString(textBuf, actual);
1733     }
1734 }
1735 
TEST_F(GreedyLineBreakerTest,testBreakWithoutBounds_trail)1736 TEST_F(GreedyLineBreakerTest, testBreakWithoutBounds_trail) {
1737     // The OvershootTest.ttf has following coverage, extent, width and bbox.
1738     // U+0061(a): 1em, (   0, 0) - (1,   1)
1739     // U+0062(b): 1em, (   0, 0) - (1.5, 1)
1740     // U+0063(c): 1em, (   0, 0) - (2,   1)
1741     // U+0064(d): 1em, (   0, 0) - (2.5, 1)
1742     // U+0065(e): 1em, (-0.5, 0) - (1,   1)
1743     // U+0066(f): 1em, (-1.0, 0) - (1,   1)
1744     // U+0067(g): 1em, (-1.5, 0) - (1,   1)
1745     constexpr bool NO_HYPHEN = false;  // No hyphenation in this test case.
1746 
1747     const std::vector<uint16_t> textBuf = utf8ToUtf16("dddd dddd dddd dddd");
1748     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1749     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1750     // Note that disable clang-format everywhere since aligned expectation is more readable.
1751     {
1752         constexpr float LINE_WIDTH = 1000;
1753         // clang-format off
1754         std::vector<LineBreakExpectation> expect = {
1755                 {"dddd dddd dddd dddd", 190, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1756         };
1757         // clang-format on
1758 
1759         const auto actual = doLineBreakForBounds(textBuf, NO_HYPHEN, LINE_WIDTH);
1760         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1761                                                    << " vs " << std::endl
1762                                                    << toString(textBuf, actual);
1763         EXPECT_EQ(MinikinRect(0, -10, 205, 0), actual.bounds[0]);
1764     }
1765     {
1766         constexpr float LINE_WIDTH = 110;
1767         // clang-format off
1768         std::vector<LineBreakExpectation> expect = {
1769                 {"dddd dddd ", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1770                 {"dddd dddd", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1771         };
1772         // clang-format on
1773 
1774         const auto actual = doLineBreakForBounds(textBuf, NO_HYPHEN, LINE_WIDTH);
1775         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1776                                                    << " vs " << std::endl
1777                                                    << toString(textBuf, actual);
1778         EXPECT_EQ(MinikinRect(0, -10, 105, 0), actual.bounds[0]);
1779         EXPECT_EQ(MinikinRect(0, -10, 105, 0), actual.bounds[1]);
1780     }
1781     {
1782         constexpr float LINE_WIDTH = 100;
1783         // Even if the total advance of "dddd dddd" is 90, the width of bounding box of "dddd dddd"
1784         // is
1785         // Rect(0em, 1em, 10.5em, 0em). So "dddd dddd" is broken into two lines.
1786         // clang-format off
1787         std::vector<LineBreakExpectation> expect = {
1788                 {"dddd ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1789                 {"dddd ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1790                 {"dddd ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1791                 {"dddd", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1792         };
1793         // clang-format on
1794 
1795         const auto actual = doLineBreakForBounds(textBuf, NO_HYPHEN, LINE_WIDTH);
1796         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1797                                                    << " vs " << std::endl
1798                                                    << toString(textBuf, actual);
1799         EXPECT_EQ(MinikinRect(0, -10, 55, 0), actual.bounds[0]);
1800         EXPECT_EQ(MinikinRect(0, -10, 55, 0), actual.bounds[1]);
1801         EXPECT_EQ(MinikinRect(0, -10, 55, 0), actual.bounds[2]);
1802         EXPECT_EQ(MinikinRect(0, -10, 55, 0), actual.bounds[3]);
1803     }
1804 }
1805 
TEST_F(GreedyLineBreakerTest,testBreakWithoutBounds_preceding)1806 TEST_F(GreedyLineBreakerTest, testBreakWithoutBounds_preceding) {
1807     // The OvershootTest.ttf has following coverage, extent, width and bbox.
1808     // U+0061(a): 1em, (   0, 0) - (1,   1)
1809     // U+0062(b): 1em, (   0, 0) - (1.5, 1)
1810     // U+0063(c): 1em, (   0, 0) - (2,   1)
1811     // U+0064(d): 1em, (   0, 0) - (2.5, 1)
1812     // U+0065(e): 1em, (-0.5, 0) - (1,   1)
1813     // U+0066(f): 1em, (-1.0, 0) - (1,   1)
1814     // U+0067(g): 1em, (-1.5, 0) - (1,   1)
1815     constexpr bool NO_HYPHEN = false;  // No hyphenation in this test case.
1816 
1817     const std::vector<uint16_t> textBuf = utf8ToUtf16("gggg gggg gggg gggg");
1818     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1819     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1820     // Note that disable clang-format everywhere since aligned expectation is more readable.
1821     {
1822         constexpr float LINE_WIDTH = 1000;
1823         // clang-format off
1824         std::vector<LineBreakExpectation> expect = {
1825                 {"gggg gggg gggg gggg", 190, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1826         };
1827         // clang-format on
1828 
1829         const auto actual = doLineBreakForBounds(textBuf, NO_HYPHEN, LINE_WIDTH);
1830         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1831                                                    << " vs " << std::endl
1832                                                    << toString(textBuf, actual);
1833         EXPECT_EQ(MinikinRect(-15, -10, 190, 0), actual.bounds[0]);
1834     }
1835     {
1836         constexpr float LINE_WIDTH = 110;
1837         // clang-format off
1838         std::vector<LineBreakExpectation> expect = {
1839                 {"gggg gggg ", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1840                 {"gggg gggg" , 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1841         };
1842         // clang-format on
1843 
1844         const auto actual = doLineBreakForBounds(textBuf, NO_HYPHEN, LINE_WIDTH);
1845         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1846                                                    << " vs " << std::endl
1847                                                    << toString(textBuf, actual);
1848         EXPECT_EQ(MinikinRect(-15, -10, 90, 0), actual.bounds[0]);
1849         EXPECT_EQ(MinikinRect(-15, -10, 90, 0), actual.bounds[1]);
1850     }
1851     {
1852         constexpr float LINE_WIDTH = 100;
1853         // Even if the total advance of "dddd dddd" is 90, the width of bounding box of "dddd dddd"
1854         // is
1855         // Rect(0em, 1em, 10.5em, 0em). So "dddd dddd" is broken into two lines.
1856         // clang-format off
1857         std::vector<LineBreakExpectation> expect = {
1858                 {"gggg ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1859                 {"gggg ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1860                 {"gggg ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1861                 {"gggg" , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1862         };
1863         // clang-format on
1864 
1865         const auto actual = doLineBreakForBounds(textBuf, NO_HYPHEN, LINE_WIDTH);
1866         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1867                                                    << " vs " << std::endl
1868                                                    << toString(textBuf, actual);
1869         EXPECT_EQ(MinikinRect(-15, -10, 40, 0), actual.bounds[0]);
1870         EXPECT_EQ(MinikinRect(-15, -10, 40, 0), actual.bounds[1]);
1871         EXPECT_EQ(MinikinRect(-15, -10, 40, 0), actual.bounds[2]);
1872         EXPECT_EQ(MinikinRect(-15, -10, 40, 0), actual.bounds[3]);
1873     }
1874 }
1875 
TEST_F(GreedyLineBreakerTest,testBreakWithHyphenation_NoHyphenationSpan)1876 TEST_F(GreedyLineBreakerTest, testBreakWithHyphenation_NoHyphenationSpan) {
1877     // "hyphenation" is hyphnated to "hy-phen-a-tion".
1878     const std::vector<uint16_t> textBuf = utf8ToUtf16("This is Android. Here is hyphenation.");
1879     const Range noHyphenRange(25, 37);  // the range of the word "hyphenation".
1880 
1881     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1882     constexpr EndHyphenEdit END_HYPHEN = EndHyphenEdit::INSERT_HYPHEN;
1883     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1884 
1885     // Note that disable clang-format everywhere since aligned expectation is more readable.
1886     {
1887         constexpr float LINE_WIDTH = 100;
1888         // clang-format off
1889         std::vector<LineBreakExpectation> expect = {
1890                 { "This is "  , 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
1891                 { "Android. " , 80, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
1892                 { "Here is "  , 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
1893                 { "hyphena-"  , 80, NO_START_HYPHEN,    END_HYPHEN, ASCENT, DESCENT },
1894                 { "tion."    , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
1895         };
1896         // clang-format on
1897 
1898         const auto actual = doLineBreak(textBuf, true /* do hyphenation */, LINE_WIDTH);
1899         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1900                                                    << " vs " << std::endl
1901                                                    << toString(textBuf, actual);
1902     }
1903     {
1904         constexpr float LINE_WIDTH = 100;
1905         // clang-format off
1906         std::vector<LineBreakExpectation> expect = {
1907                 { "This is "   , 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
1908                 { "Android. "  , 80, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
1909                 { "Here is "   , 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
1910                 // Prevent hyphenation of "hyphenation". Fallback to desperate break.
1911                 { "hyphenatio" ,100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
1912                 { "n."         , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
1913         };
1914         // clang-format on
1915 
1916         const auto actual = doLineBreakWithNoHyphenSpan(textBuf, noHyphenRange, LINE_WIDTH);
1917         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1918                                                    << " vs " << std::endl
1919                                                    << toString(textBuf, actual);
1920     }
1921 }
1922 
TEST_F(GreedyLineBreakerTest,testPhraseBreakNone)1923 TEST_F(GreedyLineBreakerTest, testPhraseBreakNone) {
1924     // For short hand of writing expectation for lines.
1925     auto line = [](std::string t, float w) -> LineBreakExpectation {
1926         return {t, w, StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, ASCENT, DESCENT};
1927     };
1928 
1929     // Note that disable clang-format everywhere since aligned expectation is more readable.
1930     {
1931         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 1));
1932         constexpr float LINE_WIDTH = 100;
1933         // clang-format off
1934         std::vector<LineBreakExpectation> expect = {
1935                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002" , 80),
1936         };
1937         // clang-format on
1938 
1939         const auto actual =
1940                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::None, "ja-JP", LINE_WIDTH);
1941         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1942                                                    << " vs " << std::endl
1943                                                    << toString(textBuf, actual);
1944     }
1945     {
1946         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 2));
1947         constexpr float LINE_WIDTH = 100;
1948         // clang-format off
1949         std::vector<LineBreakExpectation> expect = {
1950                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002\u672C\u65E5" , 100),
1951                 line("\u306F\u6674\u5929\u306A\u308A\u3002" , 60),
1952         };
1953         // clang-format on
1954 
1955         const auto actual =
1956                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::None, "ja-JP", LINE_WIDTH);
1957         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1958                                                    << " vs " << std::endl
1959                                                    << toString(textBuf, actual);
1960     }
1961     {
1962         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 3));
1963         constexpr float LINE_WIDTH = 100;
1964         // clang-format off
1965         std::vector<LineBreakExpectation> expect = {
1966                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002\u672C\u65E5", 100),
1967                 line("\u306F\u6674\u5929\u306A\u308A\u3002\u672C\u65E5\u306F\u6674", 100),
1968                 line("\u5929\u306A\u308A\u3002", 40),
1969         };
1970         // clang-format on
1971 
1972         const auto actual =
1973                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::None, "ja-JP", LINE_WIDTH);
1974         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1975                                                    << " vs " << std::endl
1976                                                    << toString(textBuf, actual);
1977     }
1978     {
1979         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 4));
1980         constexpr float LINE_WIDTH = 100;
1981         // clang-format off
1982         std::vector<LineBreakExpectation> expect = {
1983                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002\u672C\u65E5", 100),
1984                 line("\u306F\u6674\u5929\u306A\u308A\u3002\u672C\u65E5\u306F\u6674", 100),
1985                 line("\u5929\u306A\u308A\u3002\u672C\u65E5\u306F\u6674\u5929\u306A", 100),
1986                 line("\u308A\u3002"  , 20),
1987         };
1988         // clang-format on
1989 
1990         const auto actual =
1991                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::None, "ja-JP", LINE_WIDTH);
1992         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1993                                                    << " vs " << std::endl
1994                                                    << toString(textBuf, actual);
1995     }
1996     {
1997         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 5));
1998         constexpr float LINE_WIDTH = 100;
1999         // clang-format off
2000         std::vector<LineBreakExpectation> expect = {
2001                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002\u672C\u65E5", 100),
2002                 line("\u306F\u6674\u5929\u306A\u308A\u3002\u672C\u65E5\u306F\u6674", 100),
2003                 line("\u5929\u306A\u308A\u3002\u672C\u65E5\u306F\u6674\u5929\u306A", 100),
2004                 line("\u308A\u3002\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 100),
2005         };
2006         // clang-format on
2007 
2008         const auto actual =
2009                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::None, "ja-JP", LINE_WIDTH);
2010         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2011                                                    << " vs " << std::endl
2012                                                    << toString(textBuf, actual);
2013     }
2014     {
2015         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 6));
2016         constexpr float LINE_WIDTH = 100;
2017         // clang-format off
2018         std::vector<LineBreakExpectation> expect = {
2019                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002\u672C\u65E5", 100),
2020                 line("\u306F\u6674\u5929\u306A\u308A\u3002\u672C\u65E5\u306F\u6674", 100),
2021                 line("\u5929\u306A\u308A\u3002\u672C\u65E5\u306F\u6674\u5929\u306A", 100),
2022                 line("\u308A\u3002\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 100),
2023                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2024         };
2025         // clang-format on
2026 
2027         const auto actual =
2028                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::None, "ja-JP", LINE_WIDTH);
2029         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2030                                                    << " vs " << std::endl
2031                                                    << toString(textBuf, actual);
2032     }
2033 }
2034 
TEST_F(GreedyLineBreakerTest,testPhraseBreakPhrase)2035 TEST_F(GreedyLineBreakerTest, testPhraseBreakPhrase) {
2036     // For short hand of writing expectation for lines.
2037     auto line = [](std::string t, float w) -> LineBreakExpectation {
2038         return {t, w, StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, ASCENT, DESCENT};
2039     };
2040 
2041     // Note that disable clang-format everywhere since aligned expectation is more readable.
2042     {
2043         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 1));
2044         constexpr float LINE_WIDTH = 100;
2045         // clang-format off
2046         std::vector<LineBreakExpectation> expect = {
2047                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2048         };
2049         // clang-format on
2050 
2051         const auto actual =
2052                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::Phrase, "ja-JP", LINE_WIDTH);
2053         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2054                                                    << " vs " << std::endl
2055                                                    << toString(textBuf, actual);
2056     }
2057     {
2058         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 2));
2059         constexpr float LINE_WIDTH = 100;
2060         // clang-format off
2061         std::vector<LineBreakExpectation> expect = {
2062                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2063                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2064         };
2065         // clang-format on
2066 
2067         const auto actual =
2068                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::Phrase, "ja-JP", LINE_WIDTH);
2069         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2070                                                    << " vs " << std::endl
2071                                                    << toString(textBuf, actual);
2072     }
2073     {
2074         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 3));
2075         constexpr float LINE_WIDTH = 100;
2076         // clang-format off
2077         std::vector<LineBreakExpectation> expect = {
2078                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2079                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2080                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2081         };
2082         // clang-format on
2083 
2084         const auto actual =
2085                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::Phrase, "ja-JP", LINE_WIDTH);
2086         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2087                                                    << " vs " << std::endl
2088                                                    << toString(textBuf, actual);
2089     }
2090     {
2091         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 4));
2092         constexpr float LINE_WIDTH = 100;
2093         // clang-format off
2094         std::vector<LineBreakExpectation> expect = {
2095                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2096                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2097                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2098                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2099         };
2100         // clang-format on
2101 
2102         const auto actual =
2103                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::Phrase, "ja-JP", LINE_WIDTH);
2104         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2105                                                    << " vs " << std::endl
2106                                                    << toString(textBuf, actual);
2107     }
2108     {
2109         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 5));
2110         constexpr float LINE_WIDTH = 100;
2111         // clang-format off
2112         std::vector<LineBreakExpectation> expect = {
2113                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2114                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2115                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2116                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2117                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2118         };
2119         // clang-format on
2120 
2121         const auto actual =
2122                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::Phrase, "ja-JP", LINE_WIDTH);
2123         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2124                                                    << " vs " << std::endl
2125                                                    << toString(textBuf, actual);
2126     }
2127     {
2128         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 6));
2129         constexpr float LINE_WIDTH = 100;
2130         // clang-format off
2131         std::vector<LineBreakExpectation> expect = {
2132                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2133                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2134                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2135                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2136                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2137                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2138         };
2139         // clang-format on
2140 
2141         const auto actual =
2142                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::Phrase, "ja-JP", LINE_WIDTH);
2143         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2144                                                    << " vs " << std::endl
2145                                                    << toString(textBuf, actual);
2146     }
2147 }
2148 
TEST_F(GreedyLineBreakerTest,testPhraseBreakAuto)2149 TEST_F(GreedyLineBreakerTest, testPhraseBreakAuto) {
2150     // For short hand of writing expectation for lines.
2151     auto line = [](std::string t, float w) -> LineBreakExpectation {
2152         return {t, w, StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, ASCENT, DESCENT};
2153     };
2154 
2155     // Note that disable clang-format everywhere since aligned expectation is more readable.
2156     {
2157         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 1));
2158         constexpr float LINE_WIDTH = 100;
2159         // clang-format off
2160         std::vector<LineBreakExpectation> expect = {
2161                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2162         };
2163         // clang-format on
2164 
2165         const auto actual =
2166                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::Auto, "ja-JP", LINE_WIDTH);
2167         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2168                                                    << " vs " << std::endl
2169                                                    << toString(textBuf, actual);
2170     }
2171     {
2172         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 2));
2173         constexpr float LINE_WIDTH = 100;
2174         // clang-format off
2175         std::vector<LineBreakExpectation> expect = {
2176                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2177                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2178         };
2179         // clang-format on
2180 
2181         const auto actual =
2182                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::Auto, "ja-JP", LINE_WIDTH);
2183         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2184                                                    << " vs " << std::endl
2185                                                    << toString(textBuf, actual);
2186     }
2187     {
2188         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 3));
2189         constexpr float LINE_WIDTH = 100;
2190         // clang-format off
2191         std::vector<LineBreakExpectation> expect = {
2192                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2193                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2194                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2195         };
2196         // clang-format on
2197 
2198         const auto actual =
2199                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::Auto, "ja-JP", LINE_WIDTH);
2200         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2201                                                    << " vs " << std::endl
2202                                                    << toString(textBuf, actual);
2203     }
2204     {
2205         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 4));
2206         constexpr float LINE_WIDTH = 100;
2207         // clang-format off
2208         std::vector<LineBreakExpectation> expect = {
2209                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2210                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2211                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2212                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2213         };
2214         // clang-format on
2215 
2216         const auto actual =
2217                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::Auto, "ja-JP", LINE_WIDTH);
2218         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2219                                                    << " vs " << std::endl
2220                                                    << toString(textBuf, actual);
2221     }
2222     // When the line becomes more or equal to 5, the phrase based line break is disabled.
2223     {
2224         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 5));
2225         constexpr float LINE_WIDTH = 100;
2226         // clang-format off
2227         std::vector<LineBreakExpectation> expect = {
2228                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002\u672C\u65E5", 100),
2229                 line("\u306F\u6674\u5929\u306A\u308A\u3002\u672C\u65E5\u306F\u6674", 100),
2230                 line("\u5929\u306A\u308A\u3002\u672C\u65E5\u306F\u6674\u5929\u306A", 100),
2231                 line("\u308A\u3002\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 100),
2232         };
2233         // clang-format on
2234 
2235         const auto actual =
2236                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::Auto, "ja-JP", LINE_WIDTH);
2237         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2238                                                    << " vs " << std::endl
2239                                                    << toString(textBuf, actual);
2240     }
2241     {
2242         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 6));
2243         constexpr float LINE_WIDTH = 100;
2244         // clang-format off
2245         std::vector<LineBreakExpectation> expect = {
2246                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002\u672C\u65E5", 100),
2247                 line("\u306F\u6674\u5929\u306A\u308A\u3002\u672C\u65E5\u306F\u6674", 100),
2248                 line("\u5929\u306A\u308A\u3002\u672C\u65E5\u306F\u6674\u5929\u306A", 100),
2249                 line("\u308A\u3002\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 100),
2250                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002"  , 80),
2251         };
2252         // clang-format on
2253 
2254         const auto actual =
2255                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::Auto, "ja-JP", LINE_WIDTH);
2256         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2257                                                    << " vs " << std::endl
2258                                                    << toString(textBuf, actual);
2259     }
2260 }
2261 
TEST_F(GreedyLineBreakerTest,testPhraseBreak_Korean)2262 TEST_F(GreedyLineBreakerTest, testPhraseBreak_Korean) {
2263     // For short hand of writing expectation for lines.
2264     auto line = [](std::string t, float w) -> LineBreakExpectation {
2265         return {t, w, StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, ASCENT, DESCENT};
2266     };
2267 
2268     // Note that disable clang-format everywhere since aligned expectation is more readable.
2269     {
2270         SCOPED_TRACE("LineBreakWOrdStyle::None should break with grapheme bounds");
2271         const std::vector<uint16_t> textBuf = utf8ToUtf16(KO_TEXT);
2272         constexpr float LINE_WIDTH = 100;
2273         // clang-format off
2274         std::vector<LineBreakExpectation> expect = {
2275                 line("\uC544\uCE68\uBC25\uC744\u0020\uBA39\uACE0\u0020\uC2F6\uC2B5", 100),
2276                 line("\uB2C8\uB2E4\u002E", 30),
2277         };
2278         // clang-format on
2279 
2280         const auto actual =
2281                 doLineBreakForKorean(textBuf, LineBreakWordStyle::None, "ko-KR", LINE_WIDTH);
2282         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2283                                                    << " vs " << std::endl
2284                                                    << toString(textBuf, actual);
2285     }
2286     {
2287         SCOPED_TRACE("LineBreakWOrdStyle::Phrase should break with spaces");
2288         const std::vector<uint16_t> textBuf = utf8ToUtf16(KO_TEXT);
2289         constexpr float LINE_WIDTH = 100;
2290         // clang-format off
2291         std::vector<LineBreakExpectation> expect = {
2292                 line("\uC544\uCE68\uBC25\uC744\u0020\uBA39\uACE0\u0020", 70),
2293                 line("\uC2F6\uC2B5\uB2C8\uB2E4\u002E", 50),
2294         };
2295         // clang-format on
2296 
2297         const auto actual =
2298                 doLineBreakForKorean(textBuf, LineBreakWordStyle::Phrase, "ko-KR", LINE_WIDTH);
2299         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2300                                                    << " vs " << std::endl
2301                                                    << toString(textBuf, actual);
2302     }
2303     {
2304         SCOPED_TRACE("LineBreakWOrdStyle::Auto should perform as phrase based line break.");
2305         const std::vector<uint16_t> textBuf = utf8ToUtf16(KO_TEXT);
2306         constexpr float LINE_WIDTH = 100;
2307         // clang-format off
2308         std::vector<LineBreakExpectation> expect = {
2309                 line("\uC544\uCE68\uBC25\uC744\u0020\uBA39\uACE0\u0020", 70),
2310                 line("\uC2F6\uC2B5\uB2C8\uB2E4\u002E", 50),
2311         };
2312         // clang-format on
2313 
2314         const auto actual =
2315                 doLineBreakForKorean(textBuf, LineBreakWordStyle::Auto, "ko-KR", LINE_WIDTH);
2316         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2317                                                    << " vs " << std::endl
2318                                                    << toString(textBuf, actual);
2319     }
2320 }
2321 
TEST_F(GreedyLineBreakerTest,testBreakWithLetterSpacing)2322 TEST_F(GreedyLineBreakerTest, testBreakWithLetterSpacing) {
2323     const std::vector<uint16_t> textBuf = utf8ToUtf16("This is an example text.");
2324 
2325     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
2326     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
2327     // Note that disable clang-format everywhere since aligned expectation is more readable.
2328     {
2329         constexpr float LINE_WIDTH = 1000;
2330         // clang-format off
2331         std::vector<LineBreakExpectation> expect = {
2332                 {"This is an example text.", 470, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2333         };
2334         // clang-format on
2335         const auto actual = doLineBreakForLetterSpacing(textBuf, 1.0f, LINE_WIDTH);
2336         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2337                                                    << " vs " << std::endl
2338                                                    << toString(textBuf, actual);
2339     }
2340     {
2341         constexpr float LINE_WIDTH = 470;
2342         // clang-format off
2343         std::vector<LineBreakExpectation> expect = {
2344                 {"This is an example text.", 470, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2345         };
2346         // clang-format on
2347         const auto actual = doLineBreakForLetterSpacing(textBuf, 1.0f, LINE_WIDTH);
2348         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2349                                                    << " vs " << std::endl
2350                                                    << toString(textBuf, actual);
2351     }
2352     {
2353         constexpr float LINE_WIDTH = 460;
2354         // clang-format off
2355         std::vector<LineBreakExpectation> expect = {
2356                 {"This is an example ", 350, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2357                 {"text.",                90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2358         };
2359         // clang-format on
2360         const auto actual = doLineBreakForLetterSpacing(textBuf, 1.0f, LINE_WIDTH);
2361         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2362                                                    << " vs " << std::endl
2363                                                    << toString(textBuf, actual);
2364     }
2365     {
2366         constexpr float LINE_WIDTH = 240;
2367         // clang-format off
2368         std::vector<LineBreakExpectation> expect = {
2369                 {"This is an ", 190, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2370                 {"example ",    130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2371                 {"text.",        90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2372         };
2373         // clang-format on
2374         const auto actual = doLineBreakForLetterSpacing(textBuf, 1.0f, LINE_WIDTH);
2375         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2376                                                    << " vs " << std::endl
2377                                                    << toString(textBuf, actual);
2378     }
2379     {
2380         constexpr float LINE_WIDTH = 130;
2381         // clang-format off
2382         std::vector<LineBreakExpectation> expect = {
2383                 {"This is ", 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2384                 {"an ",       30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2385                 {"example ", 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2386                 {"text.",     90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2387         };
2388         // clang-format on
2389         const auto actual = doLineBreakForLetterSpacing(textBuf, 1.0f, LINE_WIDTH);
2390         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2391                                                    << " vs " << std::endl
2392                                                    << toString(textBuf, actual);
2393     }
2394     {
2395         constexpr float LINE_WIDTH = 120;
2396         // clang-format off
2397         std::vector<LineBreakExpectation> expect = {
2398                 {"This ",   70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2399                 {"is an ",  90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2400                 {"exampl", 110, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2401                 {"e ",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2402                 {"text.",   90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2403         };
2404         // clang-format on
2405         const auto actual = doLineBreakForLetterSpacing(textBuf, 1.0f, LINE_WIDTH);
2406         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2407                                                    << " vs " << std::endl
2408                                                    << toString(textBuf, actual);
2409     }
2410     {
2411         constexpr float LINE_WIDTH = 30;
2412         // clang-format off
2413         std::vector<LineBreakExpectation> expect = {
2414                 {"Th",  30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2415                 {"is ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2416                 {"is ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2417                 {"an ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2418                 {"ex",  30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2419                 {"am",  30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2420                 {"pl",  30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2421                 {"e ",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2422                 {"te",  30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2423                 {"xt",  30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2424                 {".",   10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2425         };
2426         // clang-format on
2427         const auto actual = doLineBreakForLetterSpacing(textBuf, 1.0f, LINE_WIDTH);
2428         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2429                                                    << " vs " << std::endl
2430                                                    << toString(textBuf, actual);
2431     }
2432     {
2433         constexpr float LINE_WIDTH = 10;
2434         // clang-format off
2435         std::vector<LineBreakExpectation> expect = {
2436                 {"T",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2437                 {"h",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2438                 {"i",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2439                 {"s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2440                 {"i",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2441                 {"s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2442                 {"a",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2443                 {"n ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2444                 {"e",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2445                 {"x",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2446                 {"a",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2447                 {"m",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2448                 {"p",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2449                 {"l",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2450                 {"e ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2451                 {"t",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2452                 {"e",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2453                 {"x",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2454                 {"t",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2455                 {".",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2456         };
2457         // clang-format on
2458         const auto actual = doLineBreakForLetterSpacing(textBuf, 1.0f, LINE_WIDTH);
2459         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2460                                                    << " vs " << std::endl
2461                                                    << toString(textBuf, actual);
2462     }
2463 }
2464 
2465 }  // namespace
2466 }  // namespace minikin
2467