Home | History | Annotate | Download | only in unittest
      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