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 <gtest/gtest.h> 18 19#include "ICUTestBase.h" 20#include "minikin/FontCollection.h" 21#include "minikin/Layout.h" 22#include "../util/FontTestUtils.h" 23#include "../util/UnicodeUtils.h" 24 25const char* SYSTEM_FONT_PATH = "/system/fonts/"; 26const char* SYSTEM_FONT_XML = "/system/etc/fonts.xml"; 27 28namespace minikin { 29 30const float UNTOUCHED_MARKER = 1e+38; 31 32static void expectAdvances(std::vector<float> expected, float* advances, size_t length) { 33 EXPECT_LE(expected.size(), length); 34 for (size_t i = 0; i < expected.size(); ++i) { 35 EXPECT_EQ(expected[i], advances[i]) 36 << i << "th element is different. Expected: " << expected[i] 37 << ", Actual: " << advances[i]; 38 } 39 EXPECT_EQ(UNTOUCHED_MARKER, advances[expected.size()]); 40} 41 42static void resetAdvances(float* advances, size_t length) { 43 for (size_t i = 0; i < length; ++i) { 44 advances[i] = UNTOUCHED_MARKER; 45 } 46} 47 48class LayoutTest : public ICUTestBase { 49protected: 50 LayoutTest() : mCollection(nullptr) { 51 } 52 53 virtual ~LayoutTest() {} 54 55 virtual void SetUp() override { 56 mCollection = std::shared_ptr<FontCollection>( 57 getFontCollection(SYSTEM_FONT_PATH, SYSTEM_FONT_XML)); 58 } 59 60 virtual void TearDown() override { 61 } 62 63 std::shared_ptr<FontCollection> mCollection; 64}; 65 66TEST_F(LayoutTest, doLayoutTest) { 67 MinikinPaint paint; 68 MinikinRect rect; 69 const size_t kMaxAdvanceLength = 32; 70 float advances[kMaxAdvanceLength]; 71 std::vector<float> expectedValues; 72 73 Layout layout; 74 std::vector<uint16_t> text; 75 76 // The mock implementation returns 10.0f advance and 0,0-10x10 bounds for all glyph. 77 { 78 SCOPED_TRACE("one word"); 79 text = utf8ToUtf16("oneword"); 80 layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint, 81 mCollection); 82 EXPECT_EQ(70.0f, layout.getAdvance()); 83 layout.getBounds(&rect); 84 EXPECT_EQ(0.0f, rect.mLeft); 85 EXPECT_EQ(0.0f, rect.mTop); 86 EXPECT_EQ(70.0f, rect.mRight); 87 EXPECT_EQ(10.0f, rect.mBottom); 88 resetAdvances(advances, kMaxAdvanceLength); 89 layout.getAdvances(advances); 90 expectedValues.resize(text.size()); 91 for (size_t i = 0; i < expectedValues.size(); ++i) { 92 expectedValues[i] = 10.0f; 93 } 94 expectAdvances(expectedValues, advances, kMaxAdvanceLength); 95 } 96 { 97 SCOPED_TRACE("two words"); 98 text = utf8ToUtf16("two words"); 99 layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint, 100 mCollection); 101 EXPECT_EQ(90.0f, layout.getAdvance()); 102 layout.getBounds(&rect); 103 EXPECT_EQ(0.0f, rect.mLeft); 104 EXPECT_EQ(0.0f, rect.mTop); 105 EXPECT_EQ(90.0f, rect.mRight); 106 EXPECT_EQ(10.0f, rect.mBottom); 107 resetAdvances(advances, kMaxAdvanceLength); 108 layout.getAdvances(advances); 109 expectedValues.resize(text.size()); 110 for (size_t i = 0; i < expectedValues.size(); ++i) { 111 expectedValues[i] = 10.0f; 112 } 113 expectAdvances(expectedValues, advances, kMaxAdvanceLength); 114 } 115 { 116 SCOPED_TRACE("three words"); 117 text = utf8ToUtf16("three words test"); 118 layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint, 119 mCollection); 120 EXPECT_EQ(160.0f, layout.getAdvance()); 121 layout.getBounds(&rect); 122 EXPECT_EQ(0.0f, rect.mLeft); 123 EXPECT_EQ(0.0f, rect.mTop); 124 EXPECT_EQ(160.0f, rect.mRight); 125 EXPECT_EQ(10.0f, rect.mBottom); 126 resetAdvances(advances, kMaxAdvanceLength); 127 layout.getAdvances(advances); 128 expectedValues.resize(text.size()); 129 for (size_t i = 0; i < expectedValues.size(); ++i) { 130 expectedValues[i] = 10.0f; 131 } 132 expectAdvances(expectedValues, advances, kMaxAdvanceLength); 133 } 134 { 135 SCOPED_TRACE("two spaces"); 136 text = utf8ToUtf16("two spaces"); 137 layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint, 138 mCollection); 139 EXPECT_EQ(110.0f, layout.getAdvance()); 140 layout.getBounds(&rect); 141 EXPECT_EQ(0.0f, rect.mLeft); 142 EXPECT_EQ(0.0f, rect.mTop); 143 EXPECT_EQ(110.0f, rect.mRight); 144 EXPECT_EQ(10.0f, rect.mBottom); 145 resetAdvances(advances, kMaxAdvanceLength); 146 layout.getAdvances(advances); 147 expectedValues.resize(text.size()); 148 for (size_t i = 0; i < expectedValues.size(); ++i) { 149 expectedValues[i] = 10.0f; 150 } 151 expectAdvances(expectedValues, advances, kMaxAdvanceLength); 152 } 153} 154 155TEST_F(LayoutTest, doLayoutTest_wordSpacing) { 156 MinikinPaint paint; 157 MinikinRect rect; 158 const size_t kMaxAdvanceLength = 32; 159 float advances[kMaxAdvanceLength]; 160 std::vector<float> expectedValues; 161 std::vector<uint16_t> text; 162 163 Layout layout; 164 165 paint.wordSpacing = 5.0f; 166 167 // The mock implementation returns 10.0f advance and 0,0-10x10 bounds for all glyph. 168 { 169 SCOPED_TRACE("one word"); 170 text = utf8ToUtf16("oneword"); 171 layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint, 172 mCollection); 173 EXPECT_EQ(70.0f, layout.getAdvance()); 174 layout.getBounds(&rect); 175 EXPECT_EQ(0.0f, rect.mLeft); 176 EXPECT_EQ(0.0f, rect.mTop); 177 EXPECT_EQ(70.0f, rect.mRight); 178 EXPECT_EQ(10.0f, rect.mBottom); 179 resetAdvances(advances, kMaxAdvanceLength); 180 layout.getAdvances(advances); 181 expectedValues.resize(text.size()); 182 for (size_t i = 0; i < expectedValues.size(); ++i) { 183 expectedValues[i] = 10.0f; 184 } 185 expectAdvances(expectedValues, advances, kMaxAdvanceLength); 186 } 187 { 188 SCOPED_TRACE("two words"); 189 text = utf8ToUtf16("two words"); 190 layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint, 191 mCollection); 192 EXPECT_EQ(95.0f, layout.getAdvance()); 193 layout.getBounds(&rect); 194 EXPECT_EQ(0.0f, rect.mLeft); 195 EXPECT_EQ(0.0f, rect.mTop); 196 EXPECT_EQ(95.0f, rect.mRight); 197 EXPECT_EQ(10.0f, rect.mBottom); 198 resetAdvances(advances, kMaxAdvanceLength); 199 layout.getAdvances(advances); 200 EXPECT_EQ(UNTOUCHED_MARKER, advances[text.size()]); 201 resetAdvances(advances, kMaxAdvanceLength); 202 layout.getAdvances(advances); 203 expectedValues.resize(text.size()); 204 for (size_t i = 0; i < expectedValues.size(); ++i) { 205 expectedValues[i] = 10.0f; 206 } 207 expectedValues[3] = 15.0f; 208 expectAdvances(expectedValues, advances, kMaxAdvanceLength); 209 } 210 { 211 SCOPED_TRACE("three words test"); 212 text = utf8ToUtf16("three words test"); 213 layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint, 214 mCollection); 215 EXPECT_EQ(170.0f, layout.getAdvance()); 216 layout.getBounds(&rect); 217 EXPECT_EQ(0.0f, rect.mLeft); 218 EXPECT_EQ(0.0f, rect.mTop); 219 EXPECT_EQ(170.0f, rect.mRight); 220 EXPECT_EQ(10.0f, rect.mBottom); 221 resetAdvances(advances, kMaxAdvanceLength); 222 layout.getAdvances(advances); 223 expectedValues.resize(text.size()); 224 for (size_t i = 0; i < expectedValues.size(); ++i) { 225 expectedValues[i] = 10.0f; 226 } 227 expectedValues[5] = 15.0f; 228 expectedValues[11] = 15.0f; 229 expectAdvances(expectedValues, advances, kMaxAdvanceLength); 230 } 231 { 232 SCOPED_TRACE("two spaces"); 233 text = utf8ToUtf16("two spaces"); 234 layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint, 235 mCollection); 236 EXPECT_EQ(120.0f, layout.getAdvance()); 237 layout.getBounds(&rect); 238 EXPECT_EQ(0.0f, rect.mLeft); 239 EXPECT_EQ(0.0f, rect.mTop); 240 EXPECT_EQ(120.0f, rect.mRight); 241 EXPECT_EQ(10.0f, rect.mBottom); 242 resetAdvances(advances, kMaxAdvanceLength); 243 layout.getAdvances(advances); 244 expectedValues.resize(text.size()); 245 for (size_t i = 0; i < expectedValues.size(); ++i) { 246 expectedValues[i] = 10.0f; 247 } 248 expectedValues[3] = 15.0f; 249 expectedValues[4] = 15.0f; 250 expectAdvances(expectedValues, advances, kMaxAdvanceLength); 251 } 252} 253 254TEST_F(LayoutTest, doLayoutTest_negativeWordSpacing) { 255 MinikinPaint paint; 256 MinikinRect rect; 257 const size_t kMaxAdvanceLength = 32; 258 float advances[kMaxAdvanceLength]; 259 std::vector<float> expectedValues; 260 261 Layout layout; 262 std::vector<uint16_t> text; 263 264 // Negative word spacing also should work. 265 paint.wordSpacing = -5.0f; 266 267 { 268 SCOPED_TRACE("one word"); 269 text = utf8ToUtf16("oneword"); 270 layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint, 271 mCollection); 272 EXPECT_EQ(70.0f, layout.getAdvance()); 273 layout.getBounds(&rect); 274 EXPECT_EQ(0.0f, rect.mLeft); 275 EXPECT_EQ(0.0f, rect.mTop); 276 EXPECT_EQ(70.0f, rect.mRight); 277 EXPECT_EQ(10.0f, rect.mBottom); 278 resetAdvances(advances, kMaxAdvanceLength); 279 layout.getAdvances(advances); 280 expectedValues.resize(text.size()); 281 for (size_t i = 0; i < expectedValues.size(); ++i) { 282 expectedValues[i] = 10.0f; 283 } 284 expectAdvances(expectedValues, advances, kMaxAdvanceLength); 285 } 286 { 287 SCOPED_TRACE("two words"); 288 text = utf8ToUtf16("two words"); 289 layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint, 290 mCollection); 291 EXPECT_EQ(85.0f, layout.getAdvance()); 292 layout.getBounds(&rect); 293 EXPECT_EQ(0.0f, rect.mLeft); 294 EXPECT_EQ(0.0f, rect.mTop); 295 EXPECT_EQ(85.0f, rect.mRight); 296 EXPECT_EQ(10.0f, rect.mBottom); 297 resetAdvances(advances, kMaxAdvanceLength); 298 layout.getAdvances(advances); 299 expectedValues.resize(text.size()); 300 for (size_t i = 0; i < expectedValues.size(); ++i) { 301 expectedValues[i] = 10.0f; 302 } 303 expectedValues[3] = 5.0f; 304 expectAdvances(expectedValues, advances, kMaxAdvanceLength); 305 } 306 { 307 SCOPED_TRACE("three words"); 308 text = utf8ToUtf16("three word test"); 309 layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint, 310 mCollection); 311 EXPECT_EQ(140.0f, layout.getAdvance()); 312 layout.getBounds(&rect); 313 EXPECT_EQ(0.0f, rect.mLeft); 314 EXPECT_EQ(0.0f, rect.mTop); 315 EXPECT_EQ(140.0f, rect.mRight); 316 EXPECT_EQ(10.0f, rect.mBottom); 317 resetAdvances(advances, kMaxAdvanceLength); 318 layout.getAdvances(advances); 319 expectedValues.resize(text.size()); 320 for (size_t i = 0; i < expectedValues.size(); ++i) { 321 expectedValues[i] = 10.0f; 322 } 323 expectedValues[5] = 5.0f; 324 expectedValues[10] = 5.0f; 325 expectAdvances(expectedValues, advances, kMaxAdvanceLength); 326 } 327 { 328 SCOPED_TRACE("two spaces"); 329 text = utf8ToUtf16("two spaces"); 330 layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint, 331 mCollection); 332 EXPECT_EQ(100.0f, layout.getAdvance()); 333 layout.getBounds(&rect); 334 EXPECT_EQ(0.0f, rect.mLeft); 335 EXPECT_EQ(0.0f, rect.mTop); 336 EXPECT_EQ(100.0f, rect.mRight); 337 EXPECT_EQ(10.0f, rect.mBottom); 338 resetAdvances(advances, kMaxAdvanceLength); 339 layout.getAdvances(advances); 340 expectedValues.resize(text.size()); 341 for (size_t i = 0; i < expectedValues.size(); ++i) { 342 expectedValues[i] = 10.0f; 343 } 344 expectedValues[3] = 5.0f; 345 expectedValues[4] = 5.0f; 346 expectAdvances(expectedValues, advances, kMaxAdvanceLength); 347 } 348} 349 350TEST_F(LayoutTest, doLayoutTest_rtlTest) { 351 MinikinPaint paint; 352 353 std::vector<uint16_t> text = parseUnicodeString("'a' 'b' U+3042 U+3043 'c' 'd'"); 354 355 Layout ltrLayout; 356 ltrLayout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint, 357 mCollection); 358 359 Layout rtlLayout; 360 rtlLayout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_RTL, FontStyle(), paint, 361 mCollection); 362 363 ASSERT_EQ(ltrLayout.nGlyphs(), rtlLayout.nGlyphs()); 364 ASSERT_EQ(6u, ltrLayout.nGlyphs()); 365 366 size_t nGlyphs = ltrLayout.nGlyphs(); 367 for (size_t i = 0; i < nGlyphs; ++i) { 368 EXPECT_EQ(ltrLayout.getFont(i), rtlLayout.getFont(nGlyphs - i - 1)); 369 EXPECT_EQ(ltrLayout.getGlyphId(i), rtlLayout.getGlyphId(nGlyphs - i - 1)); 370 } 371} 372 373TEST_F(LayoutTest, hyphenationTest) { 374 Layout layout; 375 std::vector<uint16_t> text; 376 377 // The mock implementation returns 10.0f advance for all glyphs. 378 { 379 SCOPED_TRACE("one word with no hyphen edit"); 380 text = utf8ToUtf16("oneword"); 381 MinikinPaint paint; 382 paint.hyphenEdit = HyphenEdit::NO_EDIT; 383 layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint, 384 mCollection); 385 EXPECT_EQ(70.0f, layout.getAdvance()); 386 } 387 { 388 SCOPED_TRACE("one word with hyphen insertion at the end"); 389 text = utf8ToUtf16("oneword"); 390 MinikinPaint paint; 391 paint.hyphenEdit = HyphenEdit::INSERT_HYPHEN_AT_END; 392 layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint, 393 mCollection); 394 EXPECT_EQ(80.0f, layout.getAdvance()); 395 } 396 { 397 SCOPED_TRACE("one word with hyphen replacement at the end"); 398 text = utf8ToUtf16("oneword"); 399 MinikinPaint paint; 400 paint.hyphenEdit = HyphenEdit::REPLACE_WITH_HYPHEN_AT_END; 401 layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint, 402 mCollection); 403 EXPECT_EQ(70.0f, layout.getAdvance()); 404 } 405 { 406 SCOPED_TRACE("one word with hyphen insertion at the start"); 407 text = utf8ToUtf16("oneword"); 408 MinikinPaint paint; 409 paint.hyphenEdit = HyphenEdit::INSERT_HYPHEN_AT_START; 410 layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint, 411 mCollection); 412 EXPECT_EQ(80.0f, layout.getAdvance()); 413 } 414 { 415 SCOPED_TRACE("one word with hyphen insertion at the both ends"); 416 text = utf8ToUtf16("oneword"); 417 MinikinPaint paint; 418 paint.hyphenEdit = HyphenEdit::INSERT_HYPHEN_AT_START | HyphenEdit::INSERT_HYPHEN_AT_END; 419 layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint, 420 mCollection); 421 EXPECT_EQ(90.0f, layout.getAdvance()); 422 } 423} 424 425// TODO: Add more test cases, e.g. measure text, letter spacing. 426 427} // namespace minikin 428