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