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