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(MinikinExtent(-100.0f, 20.0f), layout.extent());
78         EXPECT_EQ(1u, layout.fonts().size());
79         EXPECT_TRUE(layout.fontAt(0).font);
80         EXPECT_EQ(1u, layout.advances().size());
81         EXPECT_EQ(10.0f, layout.advances()[0]);
82         EXPECT_EQ(10.0f, layout.advance());
83     }
84     {
85         auto layout = buildLayout("II", {"LayoutTestFont.ttf"});
86         EXPECT_EQ(2u, layout.glyphCount());
87         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
88         EXPECT_EQ(Point(10.0f, 0), layout.pointAt(1));
89         EXPECT_EQ(MinikinExtent(-100.0f, 20.0f), layout.extent());
90         EXPECT_EQ(1u, layout.fonts().size());
91         EXPECT_TRUE(layout.fontAt(0).font);
92         EXPECT_TRUE(layout.fontAt(1).font);
93         EXPECT_EQ(layout.fontAt(0), layout.fontAt(1));
94         EXPECT_EQ(2u, layout.advances().size());
95         EXPECT_EQ(10.0f, layout.advances()[0]);
96         EXPECT_EQ(10.0f, layout.advances()[1]);
97         EXPECT_EQ(20.0f, layout.advance());
98     }
99     {
100         auto layout = buildLayout("IV", {"LayoutTestFont.ttf"});
101         EXPECT_EQ(2u, layout.glyphCount());
102         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
103         EXPECT_EQ(Point(10.0f, 0), layout.pointAt(1));
104         EXPECT_EQ(MinikinExtent(-100.0f, 20.0f), layout.extent());
105         EXPECT_EQ(1u, layout.fonts().size());
106         EXPECT_TRUE(layout.fontAt(0).font);
107         EXPECT_TRUE(layout.fontAt(1).font);
108         EXPECT_EQ(layout.fontAt(0), layout.fontAt(1));
109         EXPECT_EQ(2u, layout.advances().size());
110         EXPECT_EQ(10.0f, layout.advances()[0]);
111         EXPECT_EQ(50.0f, layout.advances()[1]);
112         EXPECT_EQ(60.0f, layout.advance());
113     }
114 }
115 
TEST(LayoutPieceTest,doLayoutTest_MultiFont)116 TEST(LayoutPieceTest, doLayoutTest_MultiFont) {
117     // See doLayoutTest for the details of LayoutTestFont.ttf
118     // The Hiragana.ttf has following coverage, extent, width and bbox.
119     // Ascender: 16em, Descender: -4em
120     // U+3042: 2em, (0, 0) - (2, 2)
121     // U+3044: 2em, (0, 0) - (2, 2)
122     // U+3046: 2em, (0, 0) - (2, 2)
123     // U+3048: 2em, (0, 0) - (2, 2)
124     // U+304A: 2em, (0, 0) - (2, 2)
125     {
126         auto layout = buildLayout("I\u3042", {"LayoutTestFont.ttf", "Hiragana.ttf"});
127         EXPECT_EQ(2u, layout.glyphCount());
128         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
129         EXPECT_EQ(Point(10.0f, 0), layout.pointAt(1));
130         EXPECT_EQ(MinikinExtent(-160.0f, 40.0f), layout.extent());
131         EXPECT_EQ(2u, layout.fonts().size());
132         EXPECT_TRUE(layout.fontAt(0).font);
133         EXPECT_TRUE(layout.fontAt(1).font);
134         EXPECT_NE(layout.fontAt(0), layout.fontAt(1));
135         EXPECT_EQ(2u, layout.advances().size());
136         EXPECT_EQ(10.0f, layout.advances()[0]);
137         EXPECT_EQ(20.0f, layout.advances()[1]);
138         EXPECT_EQ(30.0f, layout.advance());
139     }
140     {
141         auto layout = buildLayout("\u3042I", {"LayoutTestFont.ttf", "Hiragana.ttf"});
142         EXPECT_EQ(2u, layout.glyphCount());
143         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
144         EXPECT_EQ(Point(20.0f, 0), layout.pointAt(1));
145         EXPECT_EQ(MinikinExtent(-160.0f, 40.0f), layout.extent());
146         EXPECT_EQ(2u, layout.fonts().size());
147         EXPECT_TRUE(layout.fontAt(0).font);
148         EXPECT_TRUE(layout.fontAt(1).font);
149         EXPECT_NE(layout.fontAt(0), layout.fontAt(1));
150         EXPECT_EQ(2u, layout.advances().size());
151         EXPECT_EQ(20.0f, layout.advances()[0]);
152         EXPECT_EQ(10.0f, layout.advances()[1]);
153         EXPECT_EQ(30.0f, layout.advance());
154     }
155 }
156 
TEST(LayoutPieceTest,doLayoutTest_Ligature)157 TEST(LayoutPieceTest, doLayoutTest_Ligature) {
158     // Ligature.ttf support all ASCII characters.
159     // Ascender: 8em, Descender: -2em
160     // U+0020..U+007E: 1em, (0, 0) - (1, 1)
161     // Also this has ligature entry for fi as "ccmp" feature, ff as "liga" feature.
162     {
163         auto layout = buildLayout("fi", {"Ligature.ttf"});
164         EXPECT_EQ(1u, layout.glyphCount());
165         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
166         EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
167         EXPECT_EQ(1u, layout.fonts().size());
168         EXPECT_TRUE(layout.fontAt(0).font);
169         EXPECT_EQ(2u, layout.advances().size());
170         EXPECT_EQ(10.0f, layout.advances()[0]);
171         EXPECT_EQ(0.0f, layout.advances()[1]);  // Ligature assigns all width to the first char.
172         EXPECT_EQ(10.0f, layout.advance());
173     }
174     {
175         auto layout = buildLayout("ff", {"Ligature.ttf"});
176         EXPECT_EQ(1u, layout.glyphCount());
177         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
178         EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
179         EXPECT_EQ(1u, layout.fonts().size());
180         EXPECT_TRUE(layout.fontAt(0).font);
181         EXPECT_EQ(2u, layout.advances().size());
182         EXPECT_EQ(10.0f, layout.advances()[0]);
183         EXPECT_EQ(0.0f, layout.advances()[1]);  // Ligature assigns all width to the first char.
184         EXPECT_EQ(10.0f, layout.advance());
185     }
186     {
187         auto layout = buildLayout("fi", {"Ligature.ttf"}, "'liga' off");
188         EXPECT_EQ(1u, layout.glyphCount());
189         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
190         EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
191         EXPECT_EQ(1u, layout.fonts().size());
192         EXPECT_TRUE(layout.fontAt(0).font);
193         EXPECT_EQ(2u, layout.advances().size());
194         EXPECT_EQ(10.0f, layout.advances()[0]);
195         EXPECT_EQ(0.0f, layout.advances()[1]);  // Ligature assigns all width to the first char.
196         EXPECT_EQ(10.0f, layout.advance());
197     }
198     {
199         auto layout = buildLayout("ff", {"Ligature.ttf"}, "'liga' off");
200         EXPECT_EQ(2u, layout.glyphCount());
201         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
202         EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
203         EXPECT_EQ(1u, layout.fonts().size());
204         EXPECT_TRUE(layout.fontAt(0).font);
205         EXPECT_TRUE(layout.fontAt(1).font);
206         EXPECT_EQ(2u, layout.advances().size());
207         EXPECT_EQ(layout.fontAt(0), layout.fontAt(1));
208         EXPECT_EQ(10.0f, layout.advances()[0]);
209         EXPECT_EQ(10.0f, layout.advances()[1]);
210         EXPECT_EQ(20.0f, layout.advance());
211     }
212     {
213         auto layout = buildLayout("fii", {"Ligature.ttf"});
214         EXPECT_EQ(2u, layout.glyphCount());
215         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
216         EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
217         EXPECT_EQ(1u, layout.fonts().size());
218         EXPECT_TRUE(layout.fontAt(0).font);
219         EXPECT_TRUE(layout.fontAt(1).font);
220         EXPECT_EQ(layout.fontAt(0), layout.fontAt(1));
221         EXPECT_EQ(3u, layout.advances().size());
222         EXPECT_EQ(10.0f, layout.advances()[0]);
223         EXPECT_EQ(0.0f, layout.advances()[1]);  // Ligature assigns all width to the first char.
224         EXPECT_EQ(10.0f, layout.advances()[2]);
225         EXPECT_EQ(20.0f, layout.advance());
226     }
227     {
228         auto layout = buildLayout("if", {"Ligature.ttf"});
229         EXPECT_EQ(2u, layout.glyphCount());
230         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
231         EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
232         EXPECT_EQ(1u, layout.fonts().size());
233         EXPECT_TRUE(layout.fontAt(0).font);
234         EXPECT_TRUE(layout.fontAt(1).font);
235         EXPECT_EQ(layout.fontAt(0), layout.fontAt(1));
236         EXPECT_EQ(2u, layout.advances().size());
237         EXPECT_EQ(10.0f, layout.advances()[0]);
238         EXPECT_EQ(10.0f, layout.advances()[1]);
239         EXPECT_EQ(20.0f, layout.advance());
240     }
241 }
242 
243 }  // namespace
244 }  // namespace minikin
245