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