1 /* 2 * Copyright (C) 2016 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 "minikin/Layout.h" 18 19 #include <gtest/gtest.h> 20 21 #include "minikin/FontCollection.h" 22 #include "minikin/LayoutPieces.h" 23 24 #include "FontTestUtils.h" 25 #include "UnicodeUtils.h" 26 27 namespace minikin { 28 29 const float UNTOUCHED_MARKER = 1e+38; 30 31 static void expectAdvances(std::vector<float> expected, float* advances, size_t length) { 32 EXPECT_LE(expected.size(), length); 33 for (size_t i = 0; i < expected.size(); ++i) { 34 EXPECT_EQ(expected[i], advances[i]) 35 << i << "th element is different. Expected: " << expected[i] 36 << ", Actual: " << advances[i]; 37 } 38 EXPECT_EQ(UNTOUCHED_MARKER, advances[expected.size()]); 39 } 40 41 static void resetAdvances(float* advances, size_t length) { 42 for (size_t i = 0; i < length; ++i) { 43 advances[i] = UNTOUCHED_MARKER; 44 } 45 } 46 47 static Layout doLayout(const std::string& text, const MinikinPaint& paint) { 48 Layout layout; 49 auto utf16 = utf8ToUtf16(text); 50 Range range(0, utf16.size()); 51 layout.doLayout(utf16, range, Bidi::FORCE_LTR, paint, StartHyphenEdit::NO_EDIT, 52 EndHyphenEdit::NO_EDIT); 53 return layout; 54 } 55 56 static Layout doLayoutWithPrecomputedPieces(const std::string& text, const MinikinPaint& paint, 57 const LayoutPieces& pieces) { 58 Layout layout; 59 auto utf16 = utf8ToUtf16(text); 60 Range range(0, utf16.size()); 61 layout.doLayoutWithPrecomputedPieces(utf16, range, Bidi::FORCE_LTR, paint, 62 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, pieces); 63 return layout; 64 } 65 66 class LayoutTest : public testing::Test { 67 protected: 68 LayoutTest() : mCollection(nullptr) {} 69 70 virtual ~LayoutTest() {} 71 72 virtual void SetUp() override { mCollection = buildFontCollection("Ascii.ttf"); } 73 74 virtual void TearDown() override {} 75 76 std::shared_ptr<FontCollection> mCollection; 77 }; 78 79 TEST_F(LayoutTest, doLayoutTest) { 80 MinikinPaint paint(mCollection); 81 paint.size = 10.0f; // make 1em = 10px 82 MinikinRect rect; 83 const size_t kMaxAdvanceLength = 32; 84 float advances[kMaxAdvanceLength]; 85 std::vector<float> expectedValues; 86 87 Layout layout; 88 std::vector<uint16_t> text; 89 90 // The mock implementation returns 10.0f advance and 0,0-10x10 bounds for all glyph. 91 { 92 SCOPED_TRACE("one word"); 93 text = utf8ToUtf16("oneword"); 94 Range range(0, text.size()); 95 layout.doLayout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT, 96 EndHyphenEdit::NO_EDIT); 97 EXPECT_EQ(70.0f, layout.getAdvance()); 98 layout.getBounds(&rect); 99 EXPECT_EQ(0.0f, rect.mLeft); 100 EXPECT_EQ(10.0f, rect.mTop); 101 EXPECT_EQ(70.0f, rect.mRight); 102 EXPECT_EQ(0.0f, rect.mBottom); 103 resetAdvances(advances, kMaxAdvanceLength); 104 layout.getAdvances(advances); 105 expectedValues.resize(text.size()); 106 for (size_t i = 0; i < expectedValues.size(); ++i) { 107 expectedValues[i] = 10.0f; 108 } 109 expectAdvances(expectedValues, advances, kMaxAdvanceLength); 110 } 111 { 112 SCOPED_TRACE("two words"); 113 text = utf8ToUtf16("two words"); 114 Range range(0, text.size()); 115 layout.doLayout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT, 116 EndHyphenEdit::NO_EDIT); 117 EXPECT_EQ(90.0f, layout.getAdvance()); 118 layout.getBounds(&rect); 119 EXPECT_EQ(0.0f, rect.mLeft); 120 EXPECT_EQ(10.0f, rect.mTop); 121 EXPECT_EQ(90.0f, rect.mRight); 122 EXPECT_EQ(0.0f, rect.mBottom); 123 resetAdvances(advances, kMaxAdvanceLength); 124 layout.getAdvances(advances); 125 expectedValues.resize(text.size()); 126 for (size_t i = 0; i < expectedValues.size(); ++i) { 127 expectedValues[i] = 10.0f; 128 } 129 expectAdvances(expectedValues, advances, kMaxAdvanceLength); 130 } 131 { 132 SCOPED_TRACE("three words"); 133 text = utf8ToUtf16("three words test"); 134 Range range(0, text.size()); 135 layout.doLayout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT, 136 EndHyphenEdit::NO_EDIT); 137 EXPECT_EQ(160.0f, layout.getAdvance()); 138 layout.getBounds(&rect); 139 EXPECT_EQ(0.0f, rect.mLeft); 140 EXPECT_EQ(10.0f, rect.mTop); 141 EXPECT_EQ(160.0f, rect.mRight); 142 EXPECT_EQ(0.0f, rect.mBottom); 143 resetAdvances(advances, kMaxAdvanceLength); 144 layout.getAdvances(advances); 145 expectedValues.resize(text.size()); 146 for (size_t i = 0; i < expectedValues.size(); ++i) { 147 expectedValues[i] = 10.0f; 148 } 149 expectAdvances(expectedValues, advances, kMaxAdvanceLength); 150 } 151 { 152 SCOPED_TRACE("two spaces"); 153 text = utf8ToUtf16("two spaces"); 154 Range range(0, text.size()); 155 layout.doLayout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT, 156 EndHyphenEdit::NO_EDIT); 157 EXPECT_EQ(110.0f, layout.getAdvance()); 158 layout.getBounds(&rect); 159 EXPECT_EQ(0.0f, rect.mLeft); 160 EXPECT_EQ(10.0f, rect.mTop); 161 EXPECT_EQ(110.0f, rect.mRight); 162 EXPECT_EQ(0.0f, rect.mBottom); 163 resetAdvances(advances, kMaxAdvanceLength); 164 layout.getAdvances(advances); 165 expectedValues.resize(text.size()); 166 for (size_t i = 0; i < expectedValues.size(); ++i) { 167 expectedValues[i] = 10.0f; 168 } 169 expectAdvances(expectedValues, advances, kMaxAdvanceLength); 170 } 171 } 172 173 TEST_F(LayoutTest, doLayoutTest_wordSpacing) { 174 MinikinPaint paint(mCollection); 175 paint.size = 10.0f; // make 1em = 10px 176 MinikinRect rect; 177 const size_t kMaxAdvanceLength = 32; 178 float advances[kMaxAdvanceLength]; 179 std::vector<float> expectedValues; 180 std::vector<uint16_t> text; 181 182 Layout layout; 183 184 paint.wordSpacing = 5.0f; 185 186 // The mock implementation returns 10.0f advance and 0,0-10x10 bounds for all glyph. 187 { 188 SCOPED_TRACE("one word"); 189 text = utf8ToUtf16("oneword"); 190 Range range(0, text.size()); 191 layout.doLayout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT, 192 EndHyphenEdit::NO_EDIT); 193 EXPECT_EQ(70.0f, layout.getAdvance()); 194 layout.getBounds(&rect); 195 EXPECT_EQ(0.0f, rect.mLeft); 196 EXPECT_EQ(10.0f, rect.mTop); 197 EXPECT_EQ(70.0f, rect.mRight); 198 EXPECT_EQ(0.0f, rect.mBottom); 199 resetAdvances(advances, kMaxAdvanceLength); 200 layout.getAdvances(advances); 201 expectedValues.resize(text.size()); 202 for (size_t i = 0; i < expectedValues.size(); ++i) { 203 expectedValues[i] = 10.0f; 204 } 205 expectAdvances(expectedValues, advances, kMaxAdvanceLength); 206 } 207 { 208 SCOPED_TRACE("two words"); 209 text = utf8ToUtf16("two words"); 210 Range range(0, text.size()); 211 layout.doLayout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT, 212 EndHyphenEdit::NO_EDIT); 213 EXPECT_EQ(95.0f, layout.getAdvance()); 214 layout.getBounds(&rect); 215 EXPECT_EQ(0.0f, rect.mLeft); 216 EXPECT_EQ(10.0f, rect.mTop); 217 EXPECT_EQ(95.0f, rect.mRight); 218 EXPECT_EQ(0.0f, rect.mBottom); 219 resetAdvances(advances, kMaxAdvanceLength); 220 layout.getAdvances(advances); 221 EXPECT_EQ(UNTOUCHED_MARKER, advances[text.size()]); 222 resetAdvances(advances, kMaxAdvanceLength); 223 layout.getAdvances(advances); 224 expectedValues.resize(text.size()); 225 for (size_t i = 0; i < expectedValues.size(); ++i) { 226 expectedValues[i] = 10.0f; 227 } 228 expectedValues[3] = 15.0f; 229 expectAdvances(expectedValues, advances, kMaxAdvanceLength); 230 } 231 { 232 SCOPED_TRACE("three words test"); 233 text = utf8ToUtf16("three words test"); 234 Range range(0, text.size()); 235 layout.doLayout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT, 236 EndHyphenEdit::NO_EDIT); 237 EXPECT_EQ(170.0f, layout.getAdvance()); 238 layout.getBounds(&rect); 239 EXPECT_EQ(0.0f, rect.mLeft); 240 EXPECT_EQ(10.0f, rect.mTop); 241 EXPECT_EQ(170.0f, rect.mRight); 242 EXPECT_EQ(0.0f, rect.mBottom); 243 resetAdvances(advances, kMaxAdvanceLength); 244 layout.getAdvances(advances); 245 expectedValues.resize(text.size()); 246 for (size_t i = 0; i < expectedValues.size(); ++i) { 247 expectedValues[i] = 10.0f; 248 } 249 expectedValues[5] = 15.0f; 250 expectedValues[11] = 15.0f; 251 expectAdvances(expectedValues, advances, kMaxAdvanceLength); 252 } 253 { 254 SCOPED_TRACE("two spaces"); 255 text = utf8ToUtf16("two spaces"); 256 Range range(0, text.size()); 257 layout.doLayout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT, 258 EndHyphenEdit::NO_EDIT); 259 EXPECT_EQ(120.0f, layout.getAdvance()); 260 layout.getBounds(&rect); 261 EXPECT_EQ(0.0f, rect.mLeft); 262 EXPECT_EQ(10.0f, rect.mTop); 263 EXPECT_EQ(120.0f, rect.mRight); 264 EXPECT_EQ(0.0f, rect.mBottom); 265 resetAdvances(advances, kMaxAdvanceLength); 266 layout.getAdvances(advances); 267 expectedValues.resize(text.size()); 268 for (size_t i = 0; i < expectedValues.size(); ++i) { 269 expectedValues[i] = 10.0f; 270 } 271 expectedValues[3] = 15.0f; 272 expectedValues[4] = 15.0f; 273 expectAdvances(expectedValues, advances, kMaxAdvanceLength); 274 } 275 } 276 277 TEST_F(LayoutTest, doLayoutTest_negativeWordSpacing) { 278 MinikinPaint paint(mCollection); 279 paint.size = 10.0f; // make 1em = 10px 280 MinikinRect rect; 281 const size_t kMaxAdvanceLength = 32; 282 float advances[kMaxAdvanceLength]; 283 std::vector<float> expectedValues; 284 285 Layout layout; 286 std::vector<uint16_t> text; 287 288 // Negative word spacing also should work. 289 paint.wordSpacing = -5.0f; 290 291 { 292 SCOPED_TRACE("one word"); 293 text = utf8ToUtf16("oneword"); 294 Range range(0, text.size()); 295 layout.doLayout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT, 296 EndHyphenEdit::NO_EDIT); 297 EXPECT_EQ(70.0f, layout.getAdvance()); 298 layout.getBounds(&rect); 299 EXPECT_EQ(0.0f, rect.mLeft); 300 EXPECT_EQ(10.0f, rect.mTop); 301 EXPECT_EQ(70.0f, rect.mRight); 302 EXPECT_EQ(0.0f, rect.mBottom); 303 resetAdvances(advances, kMaxAdvanceLength); 304 layout.getAdvances(advances); 305 expectedValues.resize(text.size()); 306 for (size_t i = 0; i < expectedValues.size(); ++i) { 307 expectedValues[i] = 10.0f; 308 } 309 expectAdvances(expectedValues, advances, kMaxAdvanceLength); 310 } 311 { 312 SCOPED_TRACE("two words"); 313 text = utf8ToUtf16("two words"); 314 Range range(0, text.size()); 315 layout.doLayout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT, 316 EndHyphenEdit::NO_EDIT); 317 EXPECT_EQ(85.0f, layout.getAdvance()); 318 layout.getBounds(&rect); 319 EXPECT_EQ(0.0f, rect.mLeft); 320 EXPECT_EQ(10.0f, rect.mTop); 321 EXPECT_EQ(85.0f, rect.mRight); 322 EXPECT_EQ(0.0f, rect.mBottom); 323 resetAdvances(advances, kMaxAdvanceLength); 324 layout.getAdvances(advances); 325 expectedValues.resize(text.size()); 326 for (size_t i = 0; i < expectedValues.size(); ++i) { 327 expectedValues[i] = 10.0f; 328 } 329 expectedValues[3] = 5.0f; 330 expectAdvances(expectedValues, advances, kMaxAdvanceLength); 331 } 332 { 333 SCOPED_TRACE("three words"); 334 text = utf8ToUtf16("three word test"); 335 Range range(0, text.size()); 336 layout.doLayout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT, 337 EndHyphenEdit::NO_EDIT); 338 EXPECT_EQ(140.0f, layout.getAdvance()); 339 layout.getBounds(&rect); 340 EXPECT_EQ(0.0f, rect.mLeft); 341 EXPECT_EQ(10.0f, rect.mTop); 342 EXPECT_EQ(140.0f, rect.mRight); 343 EXPECT_EQ(0.0f, rect.mBottom); 344 resetAdvances(advances, kMaxAdvanceLength); 345 layout.getAdvances(advances); 346 expectedValues.resize(text.size()); 347 for (size_t i = 0; i < expectedValues.size(); ++i) { 348 expectedValues[i] = 10.0f; 349 } 350 expectedValues[5] = 5.0f; 351 expectedValues[10] = 5.0f; 352 expectAdvances(expectedValues, advances, kMaxAdvanceLength); 353 } 354 { 355 SCOPED_TRACE("two spaces"); 356 text = utf8ToUtf16("two spaces"); 357 Range range(0, text.size()); 358 layout.doLayout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT, 359 EndHyphenEdit::NO_EDIT); 360 EXPECT_EQ(100.0f, layout.getAdvance()); 361 layout.getBounds(&rect); 362 EXPECT_EQ(0.0f, rect.mLeft); 363 EXPECT_EQ(10.0f, rect.mTop); 364 EXPECT_EQ(100.0f, rect.mRight); 365 EXPECT_EQ(0.0f, rect.mBottom); 366 resetAdvances(advances, kMaxAdvanceLength); 367 layout.getAdvances(advances); 368 expectedValues.resize(text.size()); 369 for (size_t i = 0; i < expectedValues.size(); ++i) { 370 expectedValues[i] = 10.0f; 371 } 372 expectedValues[3] = 5.0f; 373 expectedValues[4] = 5.0f; 374 expectAdvances(expectedValues, advances, kMaxAdvanceLength); 375 } 376 } 377 378 // Test that a forced-RTL layout correctly mirros a forced-LTR layout. 379 TEST_F(LayoutTest, doLayoutTest_rtlTest) { 380 MinikinPaint paint(mCollection); 381 382 std::vector<uint16_t> text = parseUnicodeString("'a' 'b' U+3042 U+3043 'c' 'd'"); 383 Range range(0, text.size()); 384 385 Layout ltrLayout; 386 ltrLayout.doLayout(text, range, Bidi::FORCE_LTR, paint, StartHyphenEdit::NO_EDIT, 387 EndHyphenEdit::NO_EDIT); 388 389 Layout rtlLayout; 390 rtlLayout.doLayout(text, range, Bidi::FORCE_RTL, paint, StartHyphenEdit::NO_EDIT, 391 EndHyphenEdit::NO_EDIT); 392 393 ASSERT_EQ(ltrLayout.nGlyphs(), rtlLayout.nGlyphs()); 394 ASSERT_EQ(6u, ltrLayout.nGlyphs()); 395 396 size_t nGlyphs = ltrLayout.nGlyphs(); 397 for (size_t i = 0; i < nGlyphs; ++i) { 398 EXPECT_EQ(ltrLayout.getFont(i), rtlLayout.getFont(nGlyphs - i - 1)); 399 EXPECT_EQ(ltrLayout.getGlyphId(i), rtlLayout.getGlyphId(nGlyphs - i - 1)); 400 } 401 } 402 403 // Test that single-run RTL layouts of LTR-only text is laid out identical to an LTR layout. 404 TEST_F(LayoutTest, singleRunBidiTest) { 405 MinikinPaint paint(mCollection); 406 407 std::vector<uint16_t> text = parseUnicodeString("'1' '2' '3'"); 408 Range range(0, text.size()); 409 410 Layout ltrLayout; 411 ltrLayout.doLayout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT, 412 EndHyphenEdit::NO_EDIT); 413 414 Layout rtlLayout; 415 rtlLayout.doLayout(text, range, Bidi::RTL, paint, StartHyphenEdit::NO_EDIT, 416 EndHyphenEdit::NO_EDIT); 417 418 Layout defaultRtlLayout; 419 defaultRtlLayout.doLayout(text, range, Bidi::DEFAULT_RTL, paint, StartHyphenEdit::NO_EDIT, 420 EndHyphenEdit::NO_EDIT); 421 422 const size_t nGlyphs = ltrLayout.nGlyphs(); 423 ASSERT_EQ(3u, nGlyphs); 424 425 ASSERT_EQ(nGlyphs, rtlLayout.nGlyphs()); 426 ASSERT_EQ(nGlyphs, defaultRtlLayout.nGlyphs()); 427 428 for (size_t i = 0; i < nGlyphs; ++i) { 429 EXPECT_EQ(ltrLayout.getFont(i), rtlLayout.getFont(i)); 430 EXPECT_EQ(ltrLayout.getGlyphId(i), rtlLayout.getGlyphId(i)); 431 EXPECT_EQ(ltrLayout.getFont(i), defaultRtlLayout.getFont(i)); 432 EXPECT_EQ(ltrLayout.getGlyphId(i), defaultRtlLayout.getGlyphId(i)); 433 } 434 } 435 436 TEST_F(LayoutTest, hyphenationTest) { 437 MinikinPaint paint(mCollection); 438 paint.size = 10.0f; // make 1em = 10px 439 Layout layout; 440 std::vector<uint16_t> text; 441 442 // The mock implementation returns 10.0f advance for all glyphs. 443 { 444 SCOPED_TRACE("one word with no hyphen edit"); 445 text = utf8ToUtf16("oneword"); 446 Range range(0, text.size()); 447 layout.doLayout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT, 448 EndHyphenEdit::NO_EDIT); 449 EXPECT_EQ(70.0f, layout.getAdvance()); 450 } 451 { 452 SCOPED_TRACE("one word with hyphen insertion at the end"); 453 text = utf8ToUtf16("oneword"); 454 Range range(0, text.size()); 455 layout.doLayout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT, 456 EndHyphenEdit::INSERT_HYPHEN); 457 EXPECT_EQ(80.0f, layout.getAdvance()); 458 } 459 { 460 SCOPED_TRACE("one word with hyphen replacement at the end"); 461 text = utf8ToUtf16("oneword"); 462 Range range(0, text.size()); 463 layout.doLayout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT, 464 EndHyphenEdit::REPLACE_WITH_HYPHEN); 465 EXPECT_EQ(70.0f, layout.getAdvance()); 466 } 467 { 468 SCOPED_TRACE("one word with hyphen insertion at the start"); 469 text = utf8ToUtf16("oneword"); 470 Range range(0, text.size()); 471 layout.doLayout(text, range, Bidi::LTR, paint, StartHyphenEdit::INSERT_HYPHEN, 472 EndHyphenEdit::NO_EDIT); 473 EXPECT_EQ(80.0f, layout.getAdvance()); 474 } 475 { 476 SCOPED_TRACE("one word with hyphen insertion at the both ends"); 477 text = utf8ToUtf16("oneword"); 478 Range range(0, text.size()); 479 layout.doLayout(text, range, Bidi::LTR, paint, StartHyphenEdit::INSERT_HYPHEN, 480 EndHyphenEdit::INSERT_HYPHEN); 481 EXPECT_EQ(90.0f, layout.getAdvance()); 482 } 483 } 484 485 TEST_F(LayoutTest, verticalExtentTest) { 486 MinikinPaint paint(mCollection); 487 488 std::vector<uint16_t> text = utf8ToUtf16("ab"); 489 Range range(0, text.size()); 490 491 Layout layout; 492 layout.doLayout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT, 493 EndHyphenEdit::NO_EDIT); 494 MinikinExtent extents[text.size()]; 495 layout.getExtents(extents); 496 for (size_t i = 0; i < text.size(); i++) { 497 EXPECT_EQ(-10.0f, extents[i].ascent); 498 EXPECT_EQ(20.0f, extents[i].descent); 499 EXPECT_EQ(0.0f, extents[i].line_gap); 500 } 501 } 502 503 TEST_F(LayoutTest, measuredTextTest) { 504 // The test font has following coverage and width. 505 // U+0020: 10em 506 // U+002E (.): 10em 507 // U+0043 (C): 100em 508 // U+0049 (I): 1em 509 // U+004C (L): 50em 510 // U+0056 (V): 5em 511 // U+0058 (X): 10em 512 // U+005F (_): 0em 513 // U+FFFD (invalid surrogate will be replaced to this): 7em 514 // U+10331 (\uD800\uDF31): 10em 515 auto fc = buildFontCollection("LayoutTestFont.ttf"); 516 { 517 MinikinPaint paint(fc); 518 std::vector<uint16_t> text = utf8ToUtf16("I"); 519 std::vector<float> advances(text.size()); 520 Range range(0, text.size()); 521 EXPECT_EQ(1.0f, 522 Layout::measureText(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT, 523 EndHyphenEdit::NO_EDIT, advances.data(), nullptr, nullptr)); 524 ASSERT_EQ(1u, advances.size()); 525 EXPECT_EQ(1.0f, advances[0]); 526 } 527 { 528 MinikinPaint paint(fc); 529 std::vector<uint16_t> text = utf8ToUtf16("IV"); 530 std::vector<float> advances(text.size()); 531 Range range(0, text.size()); 532 EXPECT_EQ(6.0f, 533 Layout::measureText(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT, 534 EndHyphenEdit::NO_EDIT, advances.data(), nullptr, nullptr)); 535 ASSERT_EQ(2u, advances.size()); 536 EXPECT_EQ(1.0f, advances[0]); 537 EXPECT_EQ(5.0f, advances[1]); 538 } 539 { 540 MinikinPaint paint(fc); 541 std::vector<uint16_t> text = utf8ToUtf16("IVX"); 542 std::vector<float> advances(text.size()); 543 Range range(0, text.size()); 544 EXPECT_EQ(16.0f, 545 Layout::measureText(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT, 546 EndHyphenEdit::NO_EDIT, advances.data(), nullptr, nullptr)); 547 ASSERT_EQ(3u, advances.size()); 548 EXPECT_EQ(1.0f, advances[0]); 549 EXPECT_EQ(5.0f, advances[1]); 550 EXPECT_EQ(10.0f, advances[2]); 551 } 552 } 553 554 TEST_F(LayoutTest, doLayoutWithPrecomputedPiecesTest) { 555 float MARKER1 = 1e+16; 556 float MARKER2 = 1e+17; 557 auto fc = buildFontCollection("LayoutTestFont.ttf"); 558 MinikinPaint paint(fc); 559 { 560 LayoutPieces pieces; 561 562 Layout inLayout = doLayout("I", MinikinPaint(fc)); 563 inLayout.mAdvances[0] = MARKER1; // Modify the advance to make sure this layout is used. 564 pieces.insert(utf8ToUtf16("I"), Range(0, 1), paint, false /* dir */, 565 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, inLayout); 566 567 Layout outLayout = doLayoutWithPrecomputedPieces("I", MinikinPaint(fc), pieces); 568 EXPECT_EQ(MARKER1, outLayout.mAdvances[0]); 569 } 570 { 571 LayoutPieces pieces; 572 573 Layout inLayout = doLayout("I", MinikinPaint(fc)); 574 inLayout.mAdvances[0] = MARKER1; 575 pieces.insert(utf8ToUtf16("I"), Range(0, 1), paint, false /* dir */, 576 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, inLayout); 577 578 Layout outLayout = doLayoutWithPrecomputedPieces("II", MinikinPaint(fc), pieces); 579 // The layout pieces are used in word units. Should not be used "I" for "II". 580 EXPECT_NE(MARKER1, outLayout.mAdvances[0]); 581 EXPECT_NE(MARKER1, outLayout.mAdvances[1]); 582 } 583 { 584 LayoutPieces pieces; 585 586 Layout inLayout = doLayout("I", MinikinPaint(fc)); 587 inLayout.mAdvances[0] = MARKER1; 588 pieces.insert(utf8ToUtf16("I"), Range(0, 1), paint, false /* dir */, 589 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, inLayout); 590 591 Layout outLayout = doLayoutWithPrecomputedPieces("I I", MinikinPaint(fc), pieces); 592 EXPECT_EQ(MARKER1, outLayout.mAdvances[0]); 593 EXPECT_EQ(MARKER1, outLayout.mAdvances[2]); 594 } 595 { 596 LayoutPieces pieces; 597 598 Layout inLayout = doLayout("I", MinikinPaint(fc)); 599 inLayout.mAdvances[0] = MARKER1; // Modify the advance to make sure this layout is used. 600 pieces.insert(utf8ToUtf16("I"), Range(0, 1), paint, false /* dir */, 601 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, inLayout); 602 603 inLayout = doLayout("V", MinikinPaint(fc)); 604 inLayout.mAdvances[0] = MARKER2; // Modify the advance to make sure this layout is used. 605 pieces.insert(utf8ToUtf16("V"), Range(0, 1), paint, false /* dir */, 606 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, inLayout); 607 608 Layout outLayout = doLayoutWithPrecomputedPieces("I V", MinikinPaint(fc), pieces); 609 EXPECT_EQ(MARKER1, outLayout.mAdvances[0]); 610 EXPECT_EQ(MARKER2, outLayout.mAdvances[2]); 611 } 612 } 613 614 // TODO: Add more test cases, e.g. measure text, letter spacing. 615 616 } // namespace minikin 617