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 Range & range,const MinikinPaint & paint)36 static LayoutPiece buildLayout(const std::string& text, const Range& range,
37                                const MinikinPaint& paint) {
38     auto utf16 = utf8ToUtf16(text);
39     return LayoutPiece(utf16, range, false /* rtl */, paint, StartHyphenEdit::NO_EDIT,
40                        EndHyphenEdit::NO_EDIT);
41 }
42 
buildLayout(const std::string & text,std::shared_ptr<FontCollection> fc)43 static LayoutPiece buildLayout(const std::string& text, std::shared_ptr<FontCollection> fc) {
44     MinikinPaint paint(fc);
45     paint.size = 10.0f;  // make 1em = 10px
46     return buildLayout(text, paint);
47 }
48 
buildLayoutAndBounds(const std::string & text,std::shared_ptr<FontCollection> fc)49 static std::pair<LayoutPiece, MinikinRect> buildLayoutAndBounds(
50         const std::string& text, std::shared_ptr<FontCollection> fc) {
51     MinikinPaint paint(fc);
52     paint.size = 10.0f;  // make 1em = 10px
53     auto utf16 = utf8ToUtf16(text);
54     LayoutPiece lp = LayoutPiece(utf16, Range(0, utf16.size()), false /* rtl */, paint,
55                                  StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT);
56     MinikinRect rect = LayoutPiece::calculateBounds(lp, paint);
57     return std::make_pair(lp, rect);
58 }
59 
buildLayout(const std::string & text,std::shared_ptr<FontCollection> fc,const std::string fontFeaturesSettings)60 static LayoutPiece buildLayout(const std::string& text, std::shared_ptr<FontCollection> fc,
61                                const std::string fontFeaturesSettings) {
62     MinikinPaint paint(fc);
63     paint.size = 10.0f;  // make 1em = 10px
64     paint.fontFeatureSettings = FontFeature::parse(fontFeaturesSettings);
65     return buildLayout(text, paint);
66 }
67 
makeFontCollection(std::initializer_list<std::string> fonts)68 static std::shared_ptr<FontCollection> makeFontCollection(
69         std::initializer_list<std::string> fonts) {
70     std::vector<std::shared_ptr<FontFamily>> families;
71     for (const auto& fontPath : fonts) {
72         families.push_back(buildFontFamily(fontPath));
73     }
74     return FontCollection::create(families);
75 }
76 
TEST(LayoutPieceTest,doLayoutTest)77 TEST(LayoutPieceTest, doLayoutTest) {
78     // The LayoutTestFont.ttf has following coverage, extent, width and bbox.
79     // Ascender: 10em, Descender: -2em
80     // U+0020: 10em, (0, 0) - (10, 10)
81     // U+002E (.): 10em, (0, 0) - (10, 10)
82     // U+0043 (C): 100em, (0, 0) - (100, 100)
83     // U+0049 (I): 1em, (0, 0) - (1, 1)
84     // U+004C (L): 50em, (0, 0) - (50, 50)
85     // U+0056 (V): 5em, (0, 0) - (5, 5)
86     // U+0058 (X): 10em, (0, 0) - (10, 10)
87     // U+005F (_): 0em, (0, 0) - (0, 0)
88     // U+FFFD (invalid surrogate will be replaced to this): 7em, (0, 0) - (7, 7)
89     // U+10331 (\uD800\uDF31): 10em, (0, 0) - (10, 10)
90     {
91         auto fc = makeFontCollection({"LayoutTestFont.ttf"});
92         auto layout = buildLayout("I", fc);
93         EXPECT_EQ(1u, layout.glyphCount());
94         EXPECT_EQ(1u, layout.clusterCount());
95         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
96         EXPECT_EQ(MinikinExtent(-100.0f, 20.0f), layout.extent());
97         EXPECT_EQ(1u, layout.fonts().size());
98         EXPECT_TRUE(layout.fontAt(0).font);
99         EXPECT_EQ(1u, layout.advances().size());
100         EXPECT_EQ(10.0f, layout.advances()[0]);
101         EXPECT_EQ(10.0f, layout.advance());
102     }
103     {
104         auto fc = makeFontCollection({"LayoutTestFont.ttf"});
105         auto layout = buildLayout("II", fc);
106         EXPECT_EQ(2u, layout.glyphCount());
107         EXPECT_EQ(2u, layout.clusterCount());
108         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
109         EXPECT_EQ(Point(10.0f, 0), layout.pointAt(1));
110         EXPECT_EQ(MinikinExtent(-100.0f, 20.0f), layout.extent());
111         EXPECT_EQ(1u, layout.fonts().size());
112         EXPECT_TRUE(layout.fontAt(0).font);
113         EXPECT_TRUE(layout.fontAt(1).font);
114         EXPECT_EQ(layout.fontAt(0), layout.fontAt(1));
115         EXPECT_EQ(2u, layout.advances().size());
116         EXPECT_EQ(10.0f, layout.advances()[0]);
117         EXPECT_EQ(10.0f, layout.advances()[1]);
118         EXPECT_EQ(20.0f, layout.advance());
119     }
120     {
121         auto fc = makeFontCollection({"LayoutTestFont.ttf"});
122         auto layout = buildLayout("IV", fc);
123         EXPECT_EQ(2u, layout.glyphCount());
124         EXPECT_EQ(2u, layout.clusterCount());
125         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
126         EXPECT_EQ(Point(10.0f, 0), layout.pointAt(1));
127         EXPECT_EQ(MinikinExtent(-100.0f, 20.0f), layout.extent());
128         EXPECT_EQ(1u, layout.fonts().size());
129         EXPECT_TRUE(layout.fontAt(0).font);
130         EXPECT_TRUE(layout.fontAt(1).font);
131         EXPECT_EQ(layout.fontAt(0), layout.fontAt(1));
132         EXPECT_EQ(2u, layout.advances().size());
133         EXPECT_EQ(10.0f, layout.advances()[0]);
134         EXPECT_EQ(50.0f, layout.advances()[1]);
135         EXPECT_EQ(60.0f, layout.advance());
136     }
137 }
138 
TEST(LayoutPieceTest,doLayoutTest_MultiFont)139 TEST(LayoutPieceTest, doLayoutTest_MultiFont) {
140     // See doLayoutTest for the details of LayoutTestFont.ttf
141     // The Hiragana.ttf has following coverage, extent, width and bbox.
142     // Ascender: 16em, Descender: -4em
143     // U+3042: 2em, (0, 0) - (2, 2)
144     // U+3044: 2em, (0, 0) - (2, 2)
145     // U+3046: 2em, (0, 0) - (2, 2)
146     // U+3048: 2em, (0, 0) - (2, 2)
147     // U+304A: 2em, (0, 0) - (2, 2)
148     {
149         auto fc = makeFontCollection({"LayoutTestFont.ttf", "Hiragana.ttf"});
150         auto layout = buildLayout("I\u3042", fc);
151         EXPECT_EQ(2u, layout.glyphCount());
152         EXPECT_EQ(2u, layout.clusterCount());
153         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
154         EXPECT_EQ(Point(10.0f, 0), layout.pointAt(1));
155         EXPECT_EQ(MinikinExtent(-160.0f, 40.0f), layout.extent());
156         EXPECT_EQ(2u, layout.fonts().size());
157         EXPECT_TRUE(layout.fontAt(0).font);
158         EXPECT_TRUE(layout.fontAt(1).font);
159         EXPECT_NE(layout.fontAt(0), layout.fontAt(1));
160         EXPECT_EQ(2u, layout.advances().size());
161         EXPECT_EQ(10.0f, layout.advances()[0]);
162         EXPECT_EQ(20.0f, layout.advances()[1]);
163         EXPECT_EQ(30.0f, layout.advance());
164     }
165     {
166         auto fc = makeFontCollection({"LayoutTestFont.ttf", "Hiragana.ttf"});
167         auto layout = buildLayout("\u3042I", fc);
168         EXPECT_EQ(2u, layout.glyphCount());
169         EXPECT_EQ(2u, layout.clusterCount());
170         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
171         EXPECT_EQ(Point(20.0f, 0), layout.pointAt(1));
172         EXPECT_EQ(MinikinExtent(-160.0f, 40.0f), layout.extent());
173         EXPECT_EQ(2u, layout.fonts().size());
174         EXPECT_TRUE(layout.fontAt(0).font);
175         EXPECT_TRUE(layout.fontAt(1).font);
176         EXPECT_NE(layout.fontAt(0), layout.fontAt(1));
177         EXPECT_EQ(2u, layout.advances().size());
178         EXPECT_EQ(20.0f, layout.advances()[0]);
179         EXPECT_EQ(10.0f, layout.advances()[1]);
180         EXPECT_EQ(30.0f, layout.advance());
181     }
182 }
183 
TEST(LayoutPieceTest,doLayoutTest_Ligature)184 TEST(LayoutPieceTest, doLayoutTest_Ligature) {
185     // Ligature.ttf support all ASCII characters.
186     // Ascender: 8em, Descender: -2em
187     // U+0020..U+007E: 1em, (0, 0) - (1, 1)
188     // Also this has ligature entry for fi as "ccmp" feature, ff as "liga" feature.
189     {
190         auto fc = makeFontCollection({"Ligature.ttf"});
191         auto layout = buildLayout("fi", fc);
192         EXPECT_EQ(1u, layout.glyphCount());
193         EXPECT_EQ(1u, layout.clusterCount());
194         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
195         EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
196         EXPECT_EQ(1u, layout.fonts().size());
197         EXPECT_TRUE(layout.fontAt(0).font);
198         EXPECT_EQ(2u, layout.advances().size());
199         EXPECT_EQ(10.0f, layout.advances()[0]);
200         EXPECT_EQ(0.0f, layout.advances()[1]);  // Ligature assigns all width to the first char.
201         EXPECT_EQ(10.0f, layout.advance());
202     }
203     {
204         auto fc = makeFontCollection({"Ligature.ttf"});
205         auto layout = buildLayout("ff", fc);
206         EXPECT_EQ(1u, layout.glyphCount());
207         EXPECT_EQ(1u, layout.clusterCount());
208         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
209         EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
210         EXPECT_EQ(1u, layout.fonts().size());
211         EXPECT_TRUE(layout.fontAt(0).font);
212         EXPECT_EQ(2u, layout.advances().size());
213         EXPECT_EQ(10.0f, layout.advances()[0]);
214         EXPECT_EQ(0.0f, layout.advances()[1]);  // Ligature assigns all width to the first char.
215         EXPECT_EQ(10.0f, layout.advance());
216     }
217     {
218         auto fc = makeFontCollection({"Ligature.ttf"});
219         auto layout = buildLayout("fi", fc, "'liga' off");
220         EXPECT_EQ(1u, layout.glyphCount());
221         EXPECT_EQ(1u, layout.clusterCount());
222         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
223         EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
224         EXPECT_EQ(1u, layout.fonts().size());
225         EXPECT_TRUE(layout.fontAt(0).font);
226         EXPECT_EQ(2u, layout.advances().size());
227         EXPECT_EQ(10.0f, layout.advances()[0]);
228         EXPECT_EQ(0.0f, layout.advances()[1]);  // Ligature assigns all width to the first char.
229         EXPECT_EQ(10.0f, layout.advance());
230     }
231     {
232         auto fc = makeFontCollection({"Ligature.ttf"});
233         auto layout = buildLayout("ff", fc, "'liga' off");
234         EXPECT_EQ(2u, layout.glyphCount());
235         EXPECT_EQ(2u, layout.clusterCount());
236         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
237         EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
238         EXPECT_EQ(1u, layout.fonts().size());
239         EXPECT_TRUE(layout.fontAt(0).font);
240         EXPECT_TRUE(layout.fontAt(1).font);
241         EXPECT_EQ(2u, layout.advances().size());
242         EXPECT_EQ(layout.fontAt(0), layout.fontAt(1));
243         EXPECT_EQ(10.0f, layout.advances()[0]);
244         EXPECT_EQ(10.0f, layout.advances()[1]);
245         EXPECT_EQ(20.0f, layout.advance());
246     }
247     {
248         auto fc = makeFontCollection({"Ligature.ttf"});
249         auto layout = buildLayout("fii", fc);
250         EXPECT_EQ(2u, layout.glyphCount());
251         EXPECT_EQ(2u, layout.clusterCount());
252         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
253         EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
254         EXPECT_EQ(1u, layout.fonts().size());
255         EXPECT_TRUE(layout.fontAt(0).font);
256         EXPECT_TRUE(layout.fontAt(1).font);
257         EXPECT_EQ(layout.fontAt(0), layout.fontAt(1));
258         EXPECT_EQ(3u, layout.advances().size());
259         EXPECT_EQ(10.0f, layout.advances()[0]);
260         EXPECT_EQ(0.0f, layout.advances()[1]);  // Ligature assigns all width to the first char.
261         EXPECT_EQ(10.0f, layout.advances()[2]);
262         EXPECT_EQ(20.0f, layout.advance());
263     }
264     {
265         auto fc = makeFontCollection({"Ligature.ttf"});
266         auto layout = buildLayout("if", fc);
267         EXPECT_EQ(2u, layout.glyphCount());
268         EXPECT_EQ(2u, layout.clusterCount());
269         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
270         EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
271         EXPECT_EQ(1u, layout.fonts().size());
272         EXPECT_TRUE(layout.fontAt(0).font);
273         EXPECT_TRUE(layout.fontAt(1).font);
274         EXPECT_EQ(layout.fontAt(0), layout.fontAt(1));
275         EXPECT_EQ(2u, layout.advances().size());
276         EXPECT_EQ(10.0f, layout.advances()[0]);
277         EXPECT_EQ(10.0f, layout.advances()[1]);
278         EXPECT_EQ(20.0f, layout.advance());
279     }
280 }
281 
TEST(LayoutPieceTest,doLayoutTest_Overshoot)282 TEST(LayoutPieceTest, doLayoutTest_Overshoot) {
283     // See doLayoutTest for the details of OvershootTest.ttf
284     // The OvershootTest.ttf has following coverage, extent, width and bbox.
285     // U+0061: 1em, (   0, 0) - (1,   1)
286     // U+0062: 1em, (   0, 0) - (1.5, 1)
287     // U+0063: 1em, (   0, 0) - (2,   1)
288     // U+0064: 1em, (   0, 0) - (2.5, 1)
289     // U+0065: 1em, (-0.5, 0) - (1,   1)
290     // U+0066: 1em, (-1.0, 0) - (1,   1)
291     // U+0067: 1em, (-1.5, 0) - (1,   1)
292     auto fc = makeFontCollection({"OvershootTest.ttf"});
293     {
294         auto [layout, bounds] = buildLayoutAndBounds("a", fc);
295         EXPECT_EQ(1u, layout.glyphCount());
296         EXPECT_EQ(MinikinRect(0, -10, 10, 0), bounds);
297     }
298     {
299         auto [layout, bounds] = buildLayoutAndBounds("b", fc);
300         EXPECT_EQ(1u, layout.glyphCount());
301         EXPECT_EQ(MinikinRect(0, -10, 15, 0), bounds);
302     }
303     {
304         auto [layout, bounds] = buildLayoutAndBounds("c", fc);
305         EXPECT_EQ(1u, layout.glyphCount());
306         EXPECT_EQ(MinikinRect(0, -10, 20, 0), bounds);
307     }
308     {
309         auto [layout, bounds] = buildLayoutAndBounds("d", fc);
310         EXPECT_EQ(1u, layout.glyphCount());
311         EXPECT_EQ(MinikinRect(0, -10, 25, 0), bounds);
312     }
313     {
314         auto [layout, bounds] = buildLayoutAndBounds("e", fc);
315         EXPECT_EQ(1u, layout.glyphCount());
316         EXPECT_EQ(MinikinRect(-5, -10, 10, 0), bounds);
317     }
318     {
319         auto [layout, bounds] = buildLayoutAndBounds("f", fc);
320         EXPECT_EQ(1u, layout.glyphCount());
321         EXPECT_EQ(MinikinRect(-10, -10, 10, 0), bounds);
322     }
323     {
324         auto [layout, bounds] = buildLayoutAndBounds("g", fc);
325         EXPECT_EQ(1u, layout.glyphCount());
326         EXPECT_EQ(MinikinRect(-15, -10, 10, 0), bounds);
327     }
328     {
329         auto [layout, bounds] = buildLayoutAndBounds("ag", fc);
330         EXPECT_EQ(2u, layout.glyphCount());
331         EXPECT_EQ(MinikinRect(-5, -10, 20, 0), bounds);
332     }
333     {
334         auto [layout, bounds] = buildLayoutAndBounds("ga", fc);
335         EXPECT_EQ(2u, layout.glyphCount());
336         EXPECT_EQ(MinikinRect(-15, -10, 20, 0), bounds);
337     }
338     {
339         auto [layout, bounds] = buildLayoutAndBounds("dg", fc);
340         EXPECT_EQ(2u, layout.glyphCount());
341         EXPECT_EQ(MinikinRect(-5, -10, 25, 0), bounds);
342     }
343 }
344 
TEST(LayoutPieceTest,doLayoutTest_SubString)345 TEST(LayoutPieceTest, doLayoutTest_SubString) {
346     // The LayoutTestFont.ttf has following coverage, extent, width and bbox.
347     // Ascender: 10em, Descender: -2em
348     // U+0020: 10em, (0, 0) - (10, 10)
349     // U+002E (.): 10em, (0, 0) - (10, 10)
350     // U+0043 (C): 100em, (0, 0) - (100, 100)
351     // U+0049 (I): 1em, (0, 0) - (1, 1)
352     // U+004C (L): 50em, (0, 0) - (50, 50)
353     // U+0056 (V): 5em, (0, 0) - (5, 5)
354     // U+0058 (X): 10em, (0, 0) - (10, 10)
355     // U+005F (_): 0em, (0, 0) - (0, 0)
356     // U+FFFD (invalid surrogate will be replaced to this): 7em, (0, 0) - (7, 7)
357     // U+10331 (\uD800\uDF31): 10em, (0, 0) - (10, 10)
358     auto fc = makeFontCollection({"LayoutTestFont.ttf"});
359     MinikinPaint paint(fc);
360     paint.size = 10.0f;  // make 1em = 10px
361     {
362         auto layout = buildLayout("IVX", Range(0, 2), paint);
363         EXPECT_EQ(2u, layout.glyphCount());
364         EXPECT_EQ(2u, layout.clusterCount());
365         EXPECT_EQ(1u, layout.fonts().size());
366         EXPECT_TRUE(layout.fontAt(0).font);
367         EXPECT_EQ(2u, layout.advances().size());
368         EXPECT_EQ(10.0f, layout.advances()[0]);
369         EXPECT_EQ(50.0f, layout.advances()[1]);
370         EXPECT_EQ(60.0f, layout.advance());
371     }
372     {
373         auto layout = buildLayout("IVX", Range(1, 3), paint);
374         EXPECT_EQ(2u, layout.glyphCount());
375         EXPECT_EQ(2u, layout.clusterCount());
376         EXPECT_EQ(1u, layout.fonts().size());
377         EXPECT_TRUE(layout.fontAt(0).font);
378         EXPECT_EQ(2u, layout.advances().size());
379         EXPECT_EQ(50.0f, layout.advances()[0]);
380         EXPECT_EQ(100.0f, layout.advances()[1]);
381         EXPECT_EQ(150.0f, layout.advance());
382     }
383     {
384         auto layout = buildLayout("IVX", Range(1, 2), paint);
385         EXPECT_EQ(1u, layout.glyphCount());
386         EXPECT_EQ(1u, layout.clusterCount());
387         EXPECT_EQ(1u, layout.fonts().size());
388         EXPECT_TRUE(layout.fontAt(0).font);
389         EXPECT_EQ(1u, layout.advances().size());
390         EXPECT_EQ(50.0f, layout.advances()[0]);
391         EXPECT_EQ(50.0f, layout.advance());
392     }
393 }
394 
TEST(LayoutPieceTest,doLayoutLongTextTest)395 TEST(LayoutPieceTest, doLayoutLongTextTest) {
396     auto fc = makeFontCollection({"Ascii.ttf"});
397     std::string text;
398     for (int i = 0; i < 1024; i++) {
399         text += "a";
400     }
401     auto layout = buildLayout(text, fc);
402     EXPECT_EQ(1024u, layout.glyphCount());
403     EXPECT_EQ(1024u, layout.clusterCount());
404 }
405 
406 }  // namespace
407 }  // namespace minikin
408