1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <memory> 18 19 #include <gtest/gtest.h> 20 21 #include "minikin/Hyphenator.h" 22 23 #include "FileUtils.h" 24 #include "FontTestUtils.h" 25 #include "GreedyLineBreaker.h" 26 #include "HyphenatorMap.h" 27 #include "LineBreakerTestHelper.h" 28 #include "LocaleListCache.h" 29 #include "MinikinInternal.h" 30 #include "UnicodeUtils.h" 31 #include "WordBreaker.h" 32 33 namespace minikin { 34 namespace { 35 36 using line_breaker_test_helper::ConstantRun; 37 using line_breaker_test_helper::LineBreakExpectation; 38 using line_breaker_test_helper::RectangleLineWidth; 39 using line_breaker_test_helper::sameLineBreak; 40 using line_breaker_test_helper::toString; 41 42 class GreedyLineBreakerTest : public testing::Test { 43 public: 44 GreedyLineBreakerTest() {} 45 46 virtual ~GreedyLineBreakerTest() {} 47 48 virtual void SetUp() override { 49 mHyphenationPattern = readWholeFile("/system/usr/hyphen-data/hyph-en-us.hyb"); 50 Hyphenator* hyphenator = Hyphenator::loadBinary( 51 mHyphenationPattern.data(), 2 /* min prefix */, 2 /* min suffix */, "en-US"); 52 HyphenatorMap::add("en-US", hyphenator); 53 HyphenatorMap::add("pl", Hyphenator::loadBinary(nullptr, 0, 0, "pl")); 54 } 55 56 virtual void TearDown() override { HyphenatorMap::clear(); } 57 58 protected: 59 LineBreakResult doLineBreak(const U16StringPiece& textBuffer, bool doHyphenation, 60 float charWidth, float lineWidth) { 61 return doLineBreak(textBuffer, doHyphenation, charWidth, "en-US", lineWidth); 62 } 63 64 LineBreakResult doLineBreak(const U16StringPiece& textBuffer, bool doHyphenation, 65 float charWidth, const std::string& lang, float lineWidth) { 66 MeasuredTextBuilder builder; 67 builder.addCustomRun<ConstantRun>(Range(0, textBuffer.size()), lang, charWidth); 68 std::unique_ptr<MeasuredText> measuredText = builder.build( 69 textBuffer, false /* compute hyphenation */, false /* compute full layout */); 70 RectangleLineWidth rectangleLineWidth(lineWidth); 71 TabStops tabStops(nullptr, 0, 10); 72 return breakLineGreedy(textBuffer, *measuredText, rectangleLineWidth, tabStops, 73 doHyphenation); 74 } 75 76 private: 77 std::vector<uint8_t> mHyphenationPattern; 78 }; 79 80 TEST_F(GreedyLineBreakerTest, testBreakWithoutHyphenation) { 81 constexpr float CHAR_WIDTH = 10.0; 82 constexpr bool NO_HYPHEN = false; // No hyphenation in this test case. 83 const std::vector<uint16_t> textBuf = utf8ToUtf16("This is an example text."); 84 85 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT; 86 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT; 87 // Note that disable clang-format everywhere since aligned expectation is more readable. 88 { 89 constexpr float LINE_WIDTH = 1000 * CHAR_WIDTH; 90 std::vector<LineBreakExpectation> expect = { 91 {"This is an example text.", 24 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN}, 92 }; 93 94 const auto actual = doLineBreak(textBuf, NO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 95 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 96 << " vs " << std::endl 97 << toString(textBuf, actual); 98 } 99 { 100 constexpr float LINE_WIDTH = 24 * CHAR_WIDTH; 101 std::vector<LineBreakExpectation> expect = { 102 {"This is an example text.", 24 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN}, 103 }; 104 105 const auto actual = doLineBreak(textBuf, NO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 106 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 107 << " vs " << std::endl 108 << toString(textBuf, actual); 109 } 110 { 111 constexpr float LINE_WIDTH = 23 * CHAR_WIDTH; 112 // clang-format off 113 std::vector<LineBreakExpectation> expect = { 114 { "This is an example ", 18 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 115 { "text." , 5 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 116 }; 117 // clang-format on 118 119 const auto actual = doLineBreak(textBuf, NO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 120 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 121 << " vs " << std::endl 122 << toString(textBuf, actual); 123 } 124 { 125 constexpr float LINE_WIDTH = 8 * CHAR_WIDTH; 126 // clang-format off 127 std::vector<LineBreakExpectation> expect = { 128 { "This is ", 7 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 129 { "an " , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 130 { "example ", 7 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 131 { "text." , 5 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 132 }; 133 // clang-format on 134 135 const auto actual = doLineBreak(textBuf, NO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 136 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 137 << " vs " << std::endl 138 << toString(textBuf, actual); 139 } 140 { 141 constexpr float LINE_WIDTH = 7 * CHAR_WIDTH; 142 // clang-format off 143 std::vector<LineBreakExpectation> expect = { 144 { "This is ", 7 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 145 { "an " , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 146 { "example ", 7 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 147 { "text." , 5 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 148 }; 149 // clang-format on 150 151 const auto actual = doLineBreak(textBuf, NO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 152 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 153 << " vs " << std::endl 154 << toString(textBuf, actual); 155 } 156 { 157 constexpr float LINE_WIDTH = 6 * CHAR_WIDTH; 158 // clang-format off 159 std::vector<LineBreakExpectation> expect = { 160 { "This " , 4 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 161 { "is an ", 5 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 162 { "exampl", 6 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 163 { "e " , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 164 { "text." , 5 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 165 }; 166 // clang-format on 167 168 const auto actual = doLineBreak(textBuf, NO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 169 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 170 << " vs " << std::endl 171 << toString(textBuf, actual); 172 } 173 { 174 constexpr float LINE_WIDTH = 5 * CHAR_WIDTH; 175 // clang-format off 176 std::vector<LineBreakExpectation> expect = { 177 { "This " , 4 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 178 { "is an ", 5 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 179 { "examp" , 5 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 180 { "le " , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 181 { "text." , 5 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 182 }; 183 // clang-format on 184 185 const auto actual = doLineBreak(textBuf, NO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 186 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 187 << " vs " << std::endl 188 << toString(textBuf, actual); 189 } 190 { 191 constexpr float LINE_WIDTH = 4 * CHAR_WIDTH; 192 // clang-format off 193 std::vector<LineBreakExpectation> expect = { 194 { "This " , 4 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 195 { "is " , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 196 { "an " , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 197 { "exam" , 4 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 198 { "ple " , 3 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 199 { "text" , 4 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 200 { "." , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 201 }; 202 // clang-format on 203 204 const auto actual = doLineBreak(textBuf, NO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 205 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 206 << " vs " << std::endl 207 << toString(textBuf, actual); 208 } 209 { 210 constexpr float LINE_WIDTH = 3 * CHAR_WIDTH; 211 // clang-format off 212 std::vector<LineBreakExpectation> expect = { 213 { "Thi" , 3 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 214 { "s " , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 215 { "is " , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 216 { "an " , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 217 { "exa" , 3 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 218 { "mpl" , 3 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 219 { "e " , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 220 { "tex" , 3 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 221 { "t." , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 222 }; 223 // clang-format on 224 225 const auto actual = doLineBreak(textBuf, NO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 226 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 227 << " vs " << std::endl 228 << toString(textBuf, actual); 229 } 230 { 231 constexpr float LINE_WIDTH = 2 * CHAR_WIDTH; 232 // clang-format off 233 std::vector<LineBreakExpectation> expect = { 234 { "Th" , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 235 { "is ", 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 236 { "is ", 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 237 { "an ", 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 238 { "ex" , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 239 { "am" , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 240 { "pl" , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 241 { "e " , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 242 { "te" , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 243 { "xt" , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 244 { "." , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 245 }; 246 // clang-format on 247 248 const auto actual = doLineBreak(textBuf, NO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 249 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 250 << " vs " << std::endl 251 << toString(textBuf, actual); 252 } 253 { 254 constexpr float LINE_WIDTH = 1 * CHAR_WIDTH; 255 // clang-format off 256 std::vector<LineBreakExpectation> expect = { 257 { "T" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 258 { "h" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 259 { "i" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 260 { "s ", 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 261 { "i" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 262 { "s ", 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 263 { "a" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 264 { "n ", 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 265 { "e" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 266 { "x" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 267 { "a" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 268 { "m" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 269 { "p" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 270 { "l" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 271 { "e ", 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 272 { "t" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 273 { "e" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 274 { "x" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 275 { "t" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 276 { "." , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 277 }; 278 // clang-format on 279 280 const auto actual = doLineBreak(textBuf, NO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 281 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 282 << " vs " << std::endl 283 << toString(textBuf, actual); 284 } 285 } 286 287 TEST_F(GreedyLineBreakerTest, testBreakWithHyphenation) { 288 constexpr float CHAR_WIDTH = 10.0; 289 constexpr bool NO_HYPHEN = true; // Do hyphenation in this test case. 290 // "hyphenation" is hyphnated to "hy-phen-a-tion". 291 const std::vector<uint16_t> textBuf = utf8ToUtf16("Hyphenation is hyphenation."); 292 293 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT; 294 constexpr EndHyphenEdit END_HYPHEN = EndHyphenEdit::INSERT_HYPHEN; 295 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT; 296 297 // Note that disable clang-format everywhere since aligned expectation is more readable. 298 { 299 constexpr float LINE_WIDTH = 1000 * CHAR_WIDTH; 300 std::vector<LineBreakExpectation> expect = { 301 {"Hyphenation is hyphenation.", 27 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN}, 302 }; 303 304 const auto actual = doLineBreak(textBuf, NO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 305 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 306 << " vs " << std::endl 307 << toString(textBuf, actual); 308 } 309 { 310 constexpr float LINE_WIDTH = 27 * CHAR_WIDTH; 311 std::vector<LineBreakExpectation> expect = { 312 {"Hyphenation is hyphenation.", 27 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN}, 313 }; 314 315 const auto actual = doLineBreak(textBuf, NO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 316 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 317 << " vs " << std::endl 318 << toString(textBuf, actual); 319 } 320 { 321 constexpr float LINE_WIDTH = 26 * CHAR_WIDTH; 322 // clang-format off 323 std::vector<LineBreakExpectation> expect = { 324 { "Hyphenation is " , 14 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 325 { "hyphenation." , 12 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 326 }; 327 // clang-format on 328 329 const auto actual = doLineBreak(textBuf, NO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 330 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 331 << " vs " << std::endl 332 << toString(textBuf, actual); 333 } 334 { 335 constexpr float LINE_WIDTH = 17 * CHAR_WIDTH; 336 // clang-format off 337 std::vector<LineBreakExpectation> expect = { 338 { "Hyphenation is " , 14 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 339 { "hyphenation." , 12 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 340 }; 341 // clang-format on 342 343 const auto actual = doLineBreak(textBuf, NO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 344 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 345 << " vs " << std::endl 346 << toString(textBuf, actual); 347 } 348 { 349 constexpr float LINE_WIDTH = 12 * CHAR_WIDTH; 350 // clang-format off 351 std::vector<LineBreakExpectation> expect = { 352 { "Hyphenation " , 11 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 353 { "is " , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 354 { "hyphenation." , 12 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 355 }; 356 // clang-format on 357 358 const auto actual = doLineBreak(textBuf, NO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 359 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 360 << " vs " << std::endl 361 << toString(textBuf, actual); 362 } 363 { 364 constexpr float LINE_WIDTH = 10 * CHAR_WIDTH; 365 // clang-format off 366 std::vector<LineBreakExpectation> expect = { 367 { "Hyphena-", 8 * CHAR_WIDTH, NO_START_HYPHEN, END_HYPHEN }, 368 { "tion is ", 7 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 369 { "hyphena-", 8 * CHAR_WIDTH, NO_START_HYPHEN, END_HYPHEN }, 370 { "tion." , 5 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 371 }; 372 // clang-format on 373 374 const auto actual = doLineBreak(textBuf, NO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 375 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 376 << " vs " << std::endl 377 << toString(textBuf, actual); 378 } 379 { 380 constexpr float LINE_WIDTH = 8 * CHAR_WIDTH; 381 // clang-format off 382 std::vector<LineBreakExpectation> expect = { 383 { "Hyphena-", 8 * CHAR_WIDTH, NO_START_HYPHEN, END_HYPHEN }, 384 { "tion is ", 7 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 385 { "hyphena-", 8 * CHAR_WIDTH, NO_START_HYPHEN, END_HYPHEN }, 386 { "tion." , 5 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 387 }; 388 // clang-format on 389 390 const auto actual = doLineBreak(textBuf, NO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 391 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 392 << " vs " << std::endl 393 << toString(textBuf, actual); 394 } 395 { 396 constexpr float LINE_WIDTH = 7 * CHAR_WIDTH; 397 // clang-format off 398 std::vector<LineBreakExpectation> expect = { 399 { "Hyphen-", 7 * CHAR_WIDTH, NO_START_HYPHEN, END_HYPHEN }, 400 { "ation " , 5 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 401 { "is " , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 402 { "hyphen-", 7 * CHAR_WIDTH, NO_START_HYPHEN, END_HYPHEN }, 403 { "ation." , 6 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 404 }; 405 // clang-format on 406 407 const auto actual = doLineBreak(textBuf, NO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 408 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 409 << " vs " << std::endl 410 << toString(textBuf, actual); 411 } 412 { 413 constexpr float LINE_WIDTH = 6 * CHAR_WIDTH; 414 // clang-format off 415 std::vector<LineBreakExpectation> expect = { 416 { "Hy-" , 3 * CHAR_WIDTH, NO_START_HYPHEN, END_HYPHEN }, 417 { "phena-", 6 * CHAR_WIDTH, NO_START_HYPHEN, END_HYPHEN }, 418 { "tion " , 4 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 419 { "is " , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 420 { "hy-" , 3 * CHAR_WIDTH, NO_START_HYPHEN, END_HYPHEN }, 421 { "phena-", 6 * CHAR_WIDTH, NO_START_HYPHEN, END_HYPHEN }, 422 { "tion." , 5 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 423 }; 424 // clang-format on 425 426 const auto actual = doLineBreak(textBuf, NO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 427 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 428 << " vs " << std::endl 429 << toString(textBuf, actual); 430 } 431 { 432 constexpr float LINE_WIDTH = 5 * CHAR_WIDTH; 433 // clang-format off 434 std::vector<LineBreakExpectation> expect = { 435 { "Hy-" , 3 * CHAR_WIDTH, NO_START_HYPHEN, END_HYPHEN }, 436 { "phen-" , 5 * CHAR_WIDTH, NO_START_HYPHEN, END_HYPHEN }, 437 { "ation ", 5 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 438 { "is " , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 439 { "hy-" , 3 * CHAR_WIDTH, NO_START_HYPHEN, END_HYPHEN }, 440 { "phen-" , 5 * CHAR_WIDTH, NO_START_HYPHEN, END_HYPHEN }, 441 { "a-" , 2 * CHAR_WIDTH, NO_START_HYPHEN, END_HYPHEN }, 442 { "tion." , 5 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 443 }; 444 // clang-format on 445 446 const auto actual = doLineBreak(textBuf, NO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 447 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 448 << " vs " << std::endl 449 << toString(textBuf, actual); 450 } 451 { 452 constexpr float LINE_WIDTH = 4 * CHAR_WIDTH; 453 // clang-format off 454 std::vector<LineBreakExpectation> expect = { 455 { "Hy-" , 3 * CHAR_WIDTH, NO_START_HYPHEN, END_HYPHEN }, 456 { "phen" , 4 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 457 { "a-" , 2 * CHAR_WIDTH, NO_START_HYPHEN, END_HYPHEN }, 458 { "tion ", 4 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 459 { "is " , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 460 { "hy-" , 3 * CHAR_WIDTH, NO_START_HYPHEN, END_HYPHEN }, 461 { "phen" , 4 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 462 { "a-" , 2 * CHAR_WIDTH, NO_START_HYPHEN, END_HYPHEN }, 463 { "tion" , 4 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 464 { "." , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 465 }; 466 // clang-format on 467 468 const auto actual = doLineBreak(textBuf, NO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 469 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 470 << " vs " << std::endl 471 << toString(textBuf, actual); 472 } 473 { 474 constexpr float LINE_WIDTH = 3 * CHAR_WIDTH; 475 // clang-format off 476 std::vector<LineBreakExpectation> expect = { 477 { "Hy-", 3 * CHAR_WIDTH, NO_START_HYPHEN, END_HYPHEN }, 478 { "phe", 3 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 479 { "na-", 3 * CHAR_WIDTH, NO_START_HYPHEN, END_HYPHEN }, 480 { "tio", 3 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 481 { "n " , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 482 { "is ", 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 483 { "hy-", 3 * CHAR_WIDTH, NO_START_HYPHEN, END_HYPHEN }, 484 { "phe", 3 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 485 { "na-", 3 * CHAR_WIDTH, NO_START_HYPHEN, END_HYPHEN }, 486 { "tio", 3 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 487 { "n." , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 488 }; 489 // clang-format on 490 491 const auto actual = doLineBreak(textBuf, NO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 492 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 493 << " vs " << std::endl 494 << toString(textBuf, actual); 495 } 496 { 497 constexpr float LINE_WIDTH = 2 * CHAR_WIDTH; 498 // clang-format off 499 std::vector<LineBreakExpectation> expect = { 500 { "Hy" , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 501 { "ph" , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 502 { "en" , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 503 { "a-" , 2 * CHAR_WIDTH, NO_START_HYPHEN, END_HYPHEN }, 504 { "ti" , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 505 { "on ", 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 506 { "is ", 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 507 { "hy" , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 508 { "ph" , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 509 { "en" , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 510 { "a-" , 2 * CHAR_WIDTH, NO_START_HYPHEN, END_HYPHEN }, 511 { "ti" , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 512 { "on" , 2 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 513 { "." , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 514 }; 515 // clang-format on 516 517 const auto actual = doLineBreak(textBuf, NO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 518 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 519 << " vs " << std::endl 520 << toString(textBuf, actual); 521 } 522 { 523 constexpr float LINE_WIDTH = 1 * CHAR_WIDTH; 524 // clang-format off 525 std::vector<LineBreakExpectation> expect = { 526 { "H" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 527 { "y" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 528 { "p" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 529 { "h" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 530 { "e" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 531 { "n" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 532 { "a" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 533 { "t" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 534 { "i" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 535 { "o" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 536 { "n ", 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 537 { "i" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 538 { "s ", 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 539 { "h" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 540 { "y" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 541 { "p" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 542 { "h" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 543 { "e" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 544 { "n" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 545 { "a" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 546 { "t" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 547 { "i" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 548 { "o" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 549 { "n" , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 550 { "." , 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN }, 551 }; 552 // clang-format on 553 554 const auto actual = doLineBreak(textBuf, NO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 555 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 556 << " vs " << std::endl 557 << toString(textBuf, actual); 558 } 559 } 560 561 TEST_F(GreedyLineBreakerTest, testHyphenationStartLineChange) { 562 constexpr float CHAR_WIDTH = 10.0; 563 constexpr bool DO_HYPHEN = true; // Do hyphenation in this test case. 564 // "hyphenation" is hyphnated to "hy-phen-a-tion". 565 const std::vector<uint16_t> textBuf = utf8ToUtf16("czerwono-niebieska"); 566 567 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT; 568 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT; 569 constexpr StartHyphenEdit START_HYPHEN = StartHyphenEdit::INSERT_HYPHEN; 570 571 // Note that disable clang-format everywhere since aligned expectation is more readable. 572 { 573 constexpr float LINE_WIDTH = 1000 * CHAR_WIDTH; 574 std::vector<LineBreakExpectation> expect = { 575 {"czerwono-niebieska", 18 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN}, 576 }; 577 578 const auto actual = doLineBreak(textBuf, DO_HYPHEN, CHAR_WIDTH, "pl", LINE_WIDTH); 579 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 580 << " vs " << std::endl 581 << toString(textBuf, actual); 582 } 583 { 584 constexpr float LINE_WIDTH = 18 * CHAR_WIDTH; 585 std::vector<LineBreakExpectation> expect = { 586 {"czerwono-niebieska", 18 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN}, 587 }; 588 589 const auto actual = doLineBreak(textBuf, DO_HYPHEN, CHAR_WIDTH, "pl", LINE_WIDTH); 590 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 591 << " vs " << std::endl 592 << toString(textBuf, actual); 593 } 594 { 595 constexpr float LINE_WIDTH = 13 * CHAR_WIDTH; 596 // clang-format off 597 std::vector<LineBreakExpectation> expect = { 598 {"czerwono-" , 9 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN}, 599 {"-niebieska", 10 * CHAR_WIDTH, START_HYPHEN, NO_END_HYPHEN}, 600 }; 601 // clang-format on 602 603 const auto actual = doLineBreak(textBuf, DO_HYPHEN, CHAR_WIDTH, "pl", LINE_WIDTH); 604 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 605 << " vs " << std::endl 606 << toString(textBuf, actual); 607 } 608 } 609 610 TEST_F(GreedyLineBreakerTest, testZeroWidthLine) { 611 constexpr float CHAR_WIDTH = 10.0; 612 constexpr bool DO_HYPHEN = true; // Do hyphenation in this test case. 613 constexpr float LINE_WIDTH = 0 * CHAR_WIDTH; 614 615 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT; 616 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT; 617 618 { 619 const auto textBuf = utf8ToUtf16(""); 620 std::vector<LineBreakExpectation> expect = {}; 621 const auto actual = doLineBreak(textBuf, DO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 622 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 623 << " vs " << std::endl 624 << toString(textBuf, actual); 625 } 626 { 627 const auto textBuf = utf8ToUtf16("A"); 628 std::vector<LineBreakExpectation> expect = { 629 {"A", 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN}, 630 }; 631 const auto actual = doLineBreak(textBuf, DO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 632 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 633 << " vs " << std::endl 634 << toString(textBuf, actual); 635 } 636 { 637 const auto textBuf = utf8ToUtf16("AB"); 638 std::vector<LineBreakExpectation> expect = { 639 {"A", 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN}, 640 {"B", 1 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN}, 641 }; 642 const auto actual = doLineBreak(textBuf, DO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 643 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 644 << " vs " << std::endl 645 << toString(textBuf, actual); 646 } 647 } 648 649 TEST_F(GreedyLineBreakerTest, testZeroWidthCharacter) { 650 constexpr float CHAR_WIDTH = 0.0; 651 constexpr bool DO_HYPHEN = true; // Do hyphenation in this test case. 652 653 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT; 654 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT; 655 { 656 constexpr float LINE_WIDTH = 1.0; 657 const auto textBuf = utf8ToUtf16("This is an example text."); 658 std::vector<LineBreakExpectation> expect = { 659 {"This is an example text.", 0, NO_START_HYPHEN, NO_END_HYPHEN}, 660 }; 661 const auto actual = doLineBreak(textBuf, DO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 662 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 663 << " vs " << std::endl 664 << toString(textBuf, actual); 665 } 666 { 667 constexpr float LINE_WIDTH = 0.0; 668 const auto textBuf = utf8ToUtf16("This is an example text."); 669 std::vector<LineBreakExpectation> expect = { 670 {"This is an example text.", 0, NO_START_HYPHEN, NO_END_HYPHEN}, 671 }; 672 const auto actual = doLineBreak(textBuf, DO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 673 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 674 << " vs " << std::endl 675 << toString(textBuf, actual); 676 } 677 } 678 679 TEST_F(GreedyLineBreakerTest, testLocaleSwitchTest) { 680 constexpr float CHAR_WIDTH = 10.0; 681 constexpr bool DO_HYPHEN = true; // Do hyphenation in this test case. 682 683 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT; 684 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT; 685 686 constexpr float LINE_WIDTH = 24 * CHAR_WIDTH; 687 const auto textBuf = utf8ToUtf16("This is an example text."); 688 { 689 std::vector<LineBreakExpectation> expect = { 690 {"This is an example text.", 24 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN}, 691 }; 692 693 MeasuredTextBuilder builder; 694 builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH); 695 builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "en-US", CHAR_WIDTH); 696 std::unique_ptr<MeasuredText> measuredText = builder.build( 697 textBuf, false /* compute hyphenation */, false /* compute full layout */); 698 RectangleLineWidth rectangleLineWidth(LINE_WIDTH); 699 TabStops tabStops(nullptr, 0, 0); 700 701 const auto actual = 702 breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN); 703 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 704 << " vs " << std::endl 705 << toString(textBuf, actual); 706 } 707 { 708 std::vector<LineBreakExpectation> expect = { 709 {"This is an example text.", 24 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN}, 710 }; 711 712 MeasuredTextBuilder builder; 713 builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH); 714 builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "fr-FR", CHAR_WIDTH); 715 std::unique_ptr<MeasuredText> measuredText = builder.build( 716 textBuf, false /* compute hyphenation */, false /* compute full layout */); 717 RectangleLineWidth rectangleLineWidth(LINE_WIDTH); 718 TabStops tabStops(nullptr, 0, 0); 719 720 const auto actual = 721 breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN); 722 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 723 << " vs " << std::endl 724 << toString(textBuf, actual); 725 } 726 } 727 728 TEST_F(GreedyLineBreakerTest, testEmailOrUrl) { 729 constexpr float CHAR_WIDTH = 10.0; 730 constexpr bool DO_HYPHEN = true; // Do hyphenation in this test case. 731 732 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT; 733 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT; 734 { 735 constexpr float LINE_WIDTH = 24 * CHAR_WIDTH; 736 const auto textBuf = utf8ToUtf16("This is an url: http://a.b"); 737 std::vector<LineBreakExpectation> expect = { 738 {"This is an url: ", 15 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN}, 739 {"http://a.b", 10 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN}, 740 }; 741 const auto actual = doLineBreak(textBuf, DO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 742 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 743 << " vs " << std::endl 744 << toString(textBuf, actual); 745 } 746 { 747 constexpr float LINE_WIDTH = 24 * CHAR_WIDTH; 748 const auto textBuf = utf8ToUtf16("This is an email: a (at) example.com"); 749 std::vector<LineBreakExpectation> expect = { 750 {"This is an email: ", 17 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN}, 751 {"a (at) example.com", 13 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN}, 752 }; 753 const auto actual = doLineBreak(textBuf, DO_HYPHEN, CHAR_WIDTH, LINE_WIDTH); 754 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 755 << " vs " << std::endl 756 << toString(textBuf, actual); 757 } 758 } 759 760 TEST_F(GreedyLineBreakerTest, testLocaleSwitch_InEmailOrUrl) { 761 constexpr float CHAR_WIDTH = 10.0; 762 constexpr bool DO_HYPHEN = true; // Do hyphenation in this test case. 763 764 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT; 765 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT; 766 767 constexpr float LINE_WIDTH = 24 * CHAR_WIDTH; 768 { 769 const auto textBuf = utf8ToUtf16("This is an url: http://a.b"); 770 std::vector<LineBreakExpectation> expect = { 771 {"This is an url: ", 15 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN}, 772 {"http://a.b", 10 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN}, 773 }; 774 775 MeasuredTextBuilder builder; 776 builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH); 777 builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "fr-FR", CHAR_WIDTH); 778 std::unique_ptr<MeasuredText> measuredText = builder.build( 779 textBuf, false /* compute hyphenation */, false /* compute full layout */); 780 RectangleLineWidth rectangleLineWidth(LINE_WIDTH); 781 TabStops tabStops(nullptr, 0, 0); 782 783 const auto actual = 784 breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN); 785 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 786 << " vs " << std::endl 787 << toString(textBuf, actual); 788 } 789 { 790 const auto textBuf = utf8ToUtf16("This is an email: a (at) example.com"); 791 std::vector<LineBreakExpectation> expect = { 792 {"This is an email: ", 17 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN}, 793 {"a (at) example.com", 13 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN}, 794 }; 795 796 MeasuredTextBuilder builder; 797 builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH); 798 builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "fr-FR", CHAR_WIDTH); 799 std::unique_ptr<MeasuredText> measuredText = builder.build( 800 textBuf, false /* compute hyphenation */, false /* compute full layout */); 801 RectangleLineWidth rectangleLineWidth(LINE_WIDTH); 802 TabStops tabStops(nullptr, 0, 0); 803 804 const auto actual = 805 breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN); 806 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 807 << " vs " << std::endl 808 << toString(textBuf, actual); 809 } 810 } 811 812 // b/68669534 813 TEST_F(GreedyLineBreakerTest, CrashFix_Space_Tab) { 814 constexpr float CHAR_WIDTH = 10.0; 815 constexpr bool DO_HYPHEN = true; // Do hyphenation in this test case. 816 817 constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT; 818 constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT; 819 { 820 constexpr float LINE_WIDTH = 5 * CHAR_WIDTH; 821 const auto textBuf = utf8ToUtf16("a \tb"); 822 std::vector<LineBreakExpectation> expect = { 823 {"a \tb", 4 * CHAR_WIDTH, NO_START_HYPHEN, NO_END_HYPHEN}, 824 }; 825 826 MeasuredTextBuilder builder; 827 builder.addCustomRun<ConstantRun>(Range(0, textBuf.size()), "en-US", CHAR_WIDTH); 828 std::unique_ptr<MeasuredText> measuredText = builder.build( 829 textBuf, false /* compute hyphenation */, false /* compute full layout */); 830 RectangleLineWidth rectangleLineWidth(LINE_WIDTH); 831 TabStops tabStops(nullptr, 0, CHAR_WIDTH); 832 833 const auto actual = 834 breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN); 835 EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl 836 << " vs " << std::endl 837 << toString(textBuf, actual); 838 } 839 } 840 841 } // namespace 842 } // namespace minikin 843