1 /*
2  * Copyright (C) 2018 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/LayoutCore.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 namespace {
29 
buildLayout(const std::string & text,const MinikinPaint & paint)30 static LayoutPiece buildLayout(const std::string& text, const MinikinPaint& paint) {
31     auto utf16 = utf8ToUtf16(text);
32     return LayoutPiece(utf16, Range(0, utf16.size()), false /* rtl */, paint,
33                        StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT);
34 }
35 
buildLayout(const std::string & text,const std::vector<std::string> & fonts)36 static LayoutPiece buildLayout(const std::string& text, const std::vector<std::string>& fonts) {
37     std::vector<std::shared_ptr<FontFamily>> families;
38     for (const auto& fontPath : fonts) {
39         families.push_back(buildFontFamily(fontPath));
40     }
41     auto fc = std::make_shared<FontCollection>(families);
42     MinikinPaint paint(fc);
43     paint.size = 10.0f;  // make 1em = 10px
44     return buildLayout(text, paint);
45 }
46 
buildLayout(const std::string & text,const std::vector<std::string> & fonts,const std::string fontFeaturesSettings)47 static LayoutPiece buildLayout(const std::string& text, const std::vector<std::string>& fonts,
48                                const std::string fontFeaturesSettings) {
49     std::vector<std::shared_ptr<FontFamily>> families;
50     for (const auto& fontPath : fonts) {
51         families.push_back(buildFontFamily(fontPath));
52     }
53     auto fc = std::make_shared<FontCollection>(families);
54     MinikinPaint paint(fc);
55     paint.size = 10.0f;  // make 1em = 10px
56     paint.fontFeatureSettings = fontFeaturesSettings;
57     return buildLayout(text, paint);
58 }
59 
TEST(LayoutPieceTest,doLayoutTest)60 TEST(LayoutPieceTest, doLayoutTest) {
61     // The LayoutTestFont.ttf has following coverage, extent, width and bbox.
62     // Ascender: 10em, Descender: -2em
63     // U+0020: 10em, (0, 0) - (10, 10)
64     // U+002E (.): 10em, (0, 0) - (10, 10)
65     // U+0043 (C): 100em, (0, 0) - (100, 100)
66     // U+0049 (I): 1em, (0, 0) - (1, 1)
67     // U+004C (L): 50em, (0, 0) - (50, 50)
68     // U+0056 (V): 5em, (0, 0) - (5, 5)
69     // U+0058 (X): 10em, (0, 0) - (10, 10)
70     // U+005F (_): 0em, (0, 0) - (0, 0)
71     // U+FFFD (invalid surrogate will be replaced to this): 7em, (0, 0) - (7, 7)
72     // U+10331 (\uD800\uDF31): 10em, (0, 0) - (10, 10)
73     {
74         auto layout = buildLayout("I", {"LayoutTestFont.ttf"});
75         EXPECT_EQ(1u, layout.glyphCount());
76         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
77         EXPECT_EQ(MinikinRect(0.0f, 10.0f, 10.0f, 0.0f), layout.bounds());
78         EXPECT_EQ(MinikinExtent(-100.0f, 20.0f), layout.extent());
79         EXPECT_EQ(1u, layout.fonts().size());
80         EXPECT_TRUE(layout.fontAt(0).font);
81         EXPECT_EQ(1u, layout.advances().size());
82         EXPECT_EQ(10.0f, layout.advances()[0]);
83         EXPECT_EQ(10.0f, layout.advance());
84     }
85     {
86         auto layout = buildLayout("II", {"LayoutTestFont.ttf"});
87         EXPECT_EQ(2u, layout.glyphCount());
88         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
89         EXPECT_EQ(Point(10.0f, 0), layout.pointAt(1));
90         EXPECT_EQ(MinikinRect(0.0f, 10.0f, 20.0f, 0.0f), layout.bounds());
91         EXPECT_EQ(MinikinExtent(-100.0f, 20.0f), layout.extent());
92         EXPECT_EQ(1u, layout.fonts().size());
93         EXPECT_TRUE(layout.fontAt(0).font);
94         EXPECT_TRUE(layout.fontAt(1).font);
95         EXPECT_EQ(layout.fontAt(0), layout.fontAt(1));
96         EXPECT_EQ(2u, layout.advances().size());
97         EXPECT_EQ(10.0f, layout.advances()[0]);
98         EXPECT_EQ(10.0f, layout.advances()[1]);
99         EXPECT_EQ(20.0f, layout.advance());
100     }
101     {
102         auto layout = buildLayout("IV", {"LayoutTestFont.ttf"});
103         EXPECT_EQ(2u, layout.glyphCount());
104         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
105         EXPECT_EQ(Point(10.0f, 0), layout.pointAt(1));
106         EXPECT_EQ(MinikinRect(0.0f, 10.0f, 60.0f, 0.0f), layout.bounds());
107         EXPECT_EQ(MinikinExtent(-100.0f, 20.0f), layout.extent());
108         EXPECT_EQ(1u, layout.fonts().size());
109         EXPECT_TRUE(layout.fontAt(0).font);
110         EXPECT_TRUE(layout.fontAt(1).font);
111         EXPECT_EQ(layout.fontAt(0), layout.fontAt(1));
112         EXPECT_EQ(2u, layout.advances().size());
113         EXPECT_EQ(10.0f, layout.advances()[0]);
114         EXPECT_EQ(50.0f, layout.advances()[1]);
115         EXPECT_EQ(60.0f, layout.advance());
116     }
117 }
118 
TEST(LayoutPieceTest,doLayoutTest_MultiFont)119 TEST(LayoutPieceTest, doLayoutTest_MultiFont) {
120     // See doLayoutTest for the details of LayoutTestFont.ttf
121     // The Hiragana.ttf has following coverage, extent, width and bbox.
122     // Ascender: 16em, Descender: -4em
123     // U+3042: 2em, (0, 0) - (2, 2)
124     // U+3044: 2em, (0, 0) - (2, 2)
125     // U+3046: 2em, (0, 0) - (2, 2)
126     // U+3048: 2em, (0, 0) - (2, 2)
127     // U+304A: 2em, (0, 0) - (2, 2)
128     {
129         auto layout = buildLayout("I\u3042", {"LayoutTestFont.ttf", "Hiragana.ttf"});
130         EXPECT_EQ(2u, layout.glyphCount());
131         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
132         EXPECT_EQ(Point(10.0f, 0), layout.pointAt(1));
133         EXPECT_EQ(MinikinRect(0.0f, 10.0f, 30.0f, 0.0f), layout.bounds());
134         EXPECT_EQ(MinikinExtent(-160.0f, 40.0f), layout.extent());
135         EXPECT_EQ(2u, layout.fonts().size());
136         EXPECT_TRUE(layout.fontAt(0).font);
137         EXPECT_TRUE(layout.fontAt(1).font);
138         EXPECT_NE(layout.fontAt(0), layout.fontAt(1));
139         EXPECT_EQ(2u, layout.advances().size());
140         EXPECT_EQ(10.0f, layout.advances()[0]);
141         EXPECT_EQ(20.0f, layout.advances()[1]);
142         EXPECT_EQ(30.0f, layout.advance());
143     }
144     {
145         auto layout = buildLayout("\u3042I", {"LayoutTestFont.ttf", "Hiragana.ttf"});
146         EXPECT_EQ(2u, layout.glyphCount());
147         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
148         EXPECT_EQ(Point(20.0f, 0), layout.pointAt(1));
149         EXPECT_EQ(MinikinRect(0.0f, 10.0f, 30.0f, 0.0f), layout.bounds());
150         EXPECT_EQ(MinikinExtent(-160.0f, 40.0f), layout.extent());
151         EXPECT_EQ(2u, layout.fonts().size());
152         EXPECT_TRUE(layout.fontAt(0).font);
153         EXPECT_TRUE(layout.fontAt(1).font);
154         EXPECT_NE(layout.fontAt(0), layout.fontAt(1));
155         EXPECT_EQ(2u, layout.advances().size());
156         EXPECT_EQ(20.0f, layout.advances()[0]);
157         EXPECT_EQ(10.0f, layout.advances()[1]);
158         EXPECT_EQ(30.0f, layout.advance());
159     }
160 }
161 
TEST(LayoutPieceTest,doLayoutTest_Ligature)162 TEST(LayoutPieceTest, doLayoutTest_Ligature) {
163     // Ligature.ttf support all ASCII characters.
164     // Ascender: 8em, Descender: -2em
165     // U+0020..U+007E: 1em, (0, 0) - (1, 1)
166     // Also this has ligature entry for fi as "ccmp" feature, ff as "liga" feature.
167     {
168         auto layout = buildLayout("fi", {"Ligature.ttf"});
169         EXPECT_EQ(1u, layout.glyphCount());
170         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
171         EXPECT_EQ(MinikinRect(0.0f, 10.0f, 10.0f, 0.0f), layout.bounds());
172         EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
173         EXPECT_EQ(1u, layout.fonts().size());
174         EXPECT_TRUE(layout.fontAt(0).font);
175         EXPECT_EQ(2u, layout.advances().size());
176         EXPECT_EQ(10.0f, layout.advances()[0]);
177         EXPECT_EQ(0.0f, layout.advances()[1]);  // Ligature assigns all width to the first char.
178         EXPECT_EQ(10.0f, layout.advance());
179     }
180     {
181         auto layout = buildLayout("ff", {"Ligature.ttf"});
182         EXPECT_EQ(1u, layout.glyphCount());
183         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
184         EXPECT_EQ(MinikinRect(0.0f, 10.0f, 10.0f, 0.0f), layout.bounds());
185         EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
186         EXPECT_EQ(1u, layout.fonts().size());
187         EXPECT_TRUE(layout.fontAt(0).font);
188         EXPECT_EQ(2u, layout.advances().size());
189         EXPECT_EQ(10.0f, layout.advances()[0]);
190         EXPECT_EQ(0.0f, layout.advances()[1]);  // Ligature assigns all width to the first char.
191         EXPECT_EQ(10.0f, layout.advance());
192     }
193     {
194         auto layout = buildLayout("fi", {"Ligature.ttf"}, "'liga' off");
195         EXPECT_EQ(1u, layout.glyphCount());
196         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
197         EXPECT_EQ(MinikinRect(0.0f, 10.0f, 10.0f, 0.0f), layout.bounds());
198         EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
199         EXPECT_EQ(1u, layout.fonts().size());
200         EXPECT_TRUE(layout.fontAt(0).font);
201         EXPECT_EQ(2u, layout.advances().size());
202         EXPECT_EQ(10.0f, layout.advances()[0]);
203         EXPECT_EQ(0.0f, layout.advances()[1]);  // Ligature assigns all width to the first char.
204         EXPECT_EQ(10.0f, layout.advance());
205     }
206     {
207         auto layout = buildLayout("ff", {"Ligature.ttf"}, "'liga' off");
208         EXPECT_EQ(2u, layout.glyphCount());
209         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
210         EXPECT_EQ(MinikinRect(0.0f, 10.0f, 20.0f, 0.0f), layout.bounds());
211         EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
212         EXPECT_EQ(1u, layout.fonts().size());
213         EXPECT_TRUE(layout.fontAt(0).font);
214         EXPECT_TRUE(layout.fontAt(1).font);
215         EXPECT_EQ(2u, layout.advances().size());
216         EXPECT_EQ(layout.fontAt(0), layout.fontAt(1));
217         EXPECT_EQ(10.0f, layout.advances()[0]);
218         EXPECT_EQ(10.0f, layout.advances()[1]);
219         EXPECT_EQ(20.0f, layout.advance());
220     }
221     {
222         auto layout = buildLayout("fii", {"Ligature.ttf"});
223         EXPECT_EQ(2u, layout.glyphCount());
224         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
225         EXPECT_EQ(MinikinRect(0.0f, 10.0f, 20.0f, 0.0f), layout.bounds());
226         EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
227         EXPECT_EQ(1u, layout.fonts().size());
228         EXPECT_TRUE(layout.fontAt(0).font);
229         EXPECT_TRUE(layout.fontAt(1).font);
230         EXPECT_EQ(layout.fontAt(0), layout.fontAt(1));
231         EXPECT_EQ(3u, layout.advances().size());
232         EXPECT_EQ(10.0f, layout.advances()[0]);
233         EXPECT_EQ(0.0f, layout.advances()[1]);  // Ligature assigns all width to the first char.
234         EXPECT_EQ(10.0f, layout.advances()[2]);
235         EXPECT_EQ(20.0f, layout.advance());
236     }
237     {
238         auto layout = buildLayout("if", {"Ligature.ttf"});
239         EXPECT_EQ(2u, layout.glyphCount());
240         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
241         EXPECT_EQ(MinikinRect(0.0f, 10.0f, 20.0f, 0.0f), layout.bounds());
242         EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
243         EXPECT_EQ(1u, layout.fonts().size());
244         EXPECT_TRUE(layout.fontAt(0).font);
245         EXPECT_TRUE(layout.fontAt(1).font);
246         EXPECT_EQ(layout.fontAt(0), layout.fontAt(1));
247         EXPECT_EQ(2u, layout.advances().size());
248         EXPECT_EQ(10.0f, layout.advances()[0]);
249         EXPECT_EQ(10.0f, layout.advances()[1]);
250         EXPECT_EQ(20.0f, layout.advance());
251     }
252 }
253 
254 }  // namespace
255 }  // namespace minikin
256