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