1 /*
2 * Copyright (C) 2015 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/FontFamily.h>
18
19 #include <android/log.h>
20 #include <gtest/gtest.h>
21
22 #include "FontLanguageListCache.h"
23 #include "ICUTestBase.h"
24 #include "MinikinFontForTest.h"
25 #include "MinikinInternal.h"
26
27 namespace minikin {
28
29 typedef ICUTestBase FontLanguagesTest;
30 typedef ICUTestBase FontLanguageTest;
31
createFontLanguages(const std::string & input)32 static const FontLanguages& createFontLanguages(const std::string& input) {
33 android::AutoMutex _l(gMinikinLock);
34 uint32_t langId = FontLanguageListCache::getId(input);
35 return FontLanguageListCache::getById(langId);
36 }
37
createFontLanguage(const std::string & input)38 static FontLanguage createFontLanguage(const std::string& input) {
39 android::AutoMutex _l(gMinikinLock);
40 uint32_t langId = FontLanguageListCache::getId(input);
41 return FontLanguageListCache::getById(langId)[0];
42 }
43
createFontLanguageWithoutICUSanitization(const std::string & input)44 static FontLanguage createFontLanguageWithoutICUSanitization(const std::string& input) {
45 return FontLanguage(input.c_str(), input.size());
46 }
47
makeFamily(const std::string & fontPath)48 std::shared_ptr<FontFamily> makeFamily(const std::string& fontPath) {
49 std::shared_ptr<MinikinFont> font(new MinikinFontForTest(fontPath));
50 return std::make_shared<FontFamily>(
51 std::vector<Font>({Font(font, FontStyle())}));
52 }
53
TEST_F(FontLanguageTest,basicTests)54 TEST_F(FontLanguageTest, basicTests) {
55 FontLanguage defaultLang;
56 FontLanguage emptyLang("", 0);
57 FontLanguage english = createFontLanguage("en");
58 FontLanguage french = createFontLanguage("fr");
59 FontLanguage und = createFontLanguage("und");
60 FontLanguage undZsye = createFontLanguage("und-Zsye");
61
62 EXPECT_EQ(english, english);
63 EXPECT_EQ(french, french);
64
65 EXPECT_TRUE(defaultLang != defaultLang);
66 EXPECT_TRUE(emptyLang != emptyLang);
67 EXPECT_TRUE(defaultLang != emptyLang);
68 EXPECT_TRUE(defaultLang != und);
69 EXPECT_TRUE(emptyLang != und);
70 EXPECT_TRUE(english != defaultLang);
71 EXPECT_TRUE(english != emptyLang);
72 EXPECT_TRUE(english != french);
73 EXPECT_TRUE(english != undZsye);
74 EXPECT_TRUE(und != undZsye);
75 EXPECT_TRUE(english != und);
76
77 EXPECT_TRUE(defaultLang.isUnsupported());
78 EXPECT_TRUE(emptyLang.isUnsupported());
79
80 EXPECT_FALSE(english.isUnsupported());
81 EXPECT_FALSE(french.isUnsupported());
82 EXPECT_FALSE(und.isUnsupported());
83 EXPECT_FALSE(undZsye.isUnsupported());
84 }
85
TEST_F(FontLanguageTest,getStringTest)86 TEST_F(FontLanguageTest, getStringTest) {
87 EXPECT_EQ("en-Latn-US", createFontLanguage("en").getString());
88 EXPECT_EQ("en-Latn-US", createFontLanguage("en-Latn").getString());
89
90 // Capitalized language code or lowercased script should be normalized.
91 EXPECT_EQ("en-Latn-US", createFontLanguage("EN-LATN").getString());
92 EXPECT_EQ("en-Latn-US", createFontLanguage("EN-latn").getString());
93 EXPECT_EQ("en-Latn-US", createFontLanguage("en-latn").getString());
94
95 // Invalid script should be kept.
96 EXPECT_EQ("en-Xyzt-US", createFontLanguage("en-xyzt").getString());
97
98 EXPECT_EQ("en-Latn-US", createFontLanguage("en-Latn-US").getString());
99 EXPECT_EQ("ja-Jpan-JP", createFontLanguage("ja").getString());
100 EXPECT_EQ("zh-Hant-TW", createFontLanguage("zh-TW").getString());
101 EXPECT_EQ("zh-Hant-HK", createFontLanguage("zh-HK").getString());
102 EXPECT_EQ("zh-Hant-MO", createFontLanguage("zh-MO").getString());
103 EXPECT_EQ("zh-Hans-CN", createFontLanguage("zh").getString());
104 EXPECT_EQ("zh-Hans-CN", createFontLanguage("zh-CN").getString());
105 EXPECT_EQ("zh-Hans-SG", createFontLanguage("zh-SG").getString());
106 EXPECT_EQ("und", createFontLanguage("und").getString());
107 EXPECT_EQ("und", createFontLanguage("UND").getString());
108 EXPECT_EQ("und", createFontLanguage("Und").getString());
109 EXPECT_EQ("und-Zsye", createFontLanguage("und-Zsye").getString());
110 EXPECT_EQ("und-Zsye", createFontLanguage("Und-ZSYE").getString());
111 EXPECT_EQ("und-Zsye", createFontLanguage("Und-zsye").getString());
112
113 EXPECT_EQ("de-Latn-DE", createFontLanguage("de-1901").getString());
114
115 EXPECT_EQ("es-Latn-419", createFontLanguage("es-Latn-419").getString());
116
117 // Emoji subtag is dropped from getString().
118 EXPECT_EQ("es-Latn-419", createFontLanguage("es-419-u-em-emoji").getString());
119 EXPECT_EQ("es-Latn-419", createFontLanguage("es-Latn-419-u-em-emoji").getString());
120
121 // This is not a necessary desired behavior, just known behavior.
122 EXPECT_EQ("en-Latn-US", createFontLanguage("und-Abcdefgh").getString());
123 }
124
TEST_F(FontLanguageTest,testReconstruction)125 TEST_F(FontLanguageTest, testReconstruction) {
126 EXPECT_EQ("en", createFontLanguageWithoutICUSanitization("en").getString());
127 EXPECT_EQ("fil", createFontLanguageWithoutICUSanitization("fil").getString());
128 EXPECT_EQ("und", createFontLanguageWithoutICUSanitization("und").getString());
129
130 EXPECT_EQ("en-Latn", createFontLanguageWithoutICUSanitization("en-Latn").getString());
131 EXPECT_EQ("fil-Taga", createFontLanguageWithoutICUSanitization("fil-Taga").getString());
132 EXPECT_EQ("und-Zsye", createFontLanguageWithoutICUSanitization("und-Zsye").getString());
133
134 EXPECT_EQ("en-US", createFontLanguageWithoutICUSanitization("en-US").getString());
135 EXPECT_EQ("fil-PH", createFontLanguageWithoutICUSanitization("fil-PH").getString());
136 EXPECT_EQ("es-419", createFontLanguageWithoutICUSanitization("es-419").getString());
137
138 EXPECT_EQ("en-Latn-US", createFontLanguageWithoutICUSanitization("en-Latn-US").getString());
139 EXPECT_EQ("fil-Taga-PH", createFontLanguageWithoutICUSanitization("fil-Taga-PH").getString());
140 EXPECT_EQ("es-Latn-419", createFontLanguageWithoutICUSanitization("es-Latn-419").getString());
141
142 // Possible minimum/maximum values.
143 EXPECT_EQ("aa", createFontLanguageWithoutICUSanitization("aa").getString());
144 EXPECT_EQ("zz", createFontLanguageWithoutICUSanitization("zz").getString());
145 EXPECT_EQ("aa-Aaaa", createFontLanguageWithoutICUSanitization("aa-Aaaa").getString());
146 EXPECT_EQ("zz-Zzzz", createFontLanguageWithoutICUSanitization("zz-Zzzz").getString());
147 EXPECT_EQ("aaa-Aaaa-AA", createFontLanguageWithoutICUSanitization("aaa-Aaaa-AA").getString());
148 EXPECT_EQ("zzz-Zzzz-ZZ", createFontLanguageWithoutICUSanitization("zzz-Zzzz-ZZ").getString());
149 EXPECT_EQ("aaa-Aaaa-000", createFontLanguageWithoutICUSanitization("aaa-Aaaa-000").getString());
150 EXPECT_EQ("zzz-Zzzz-999", createFontLanguageWithoutICUSanitization("zzz-Zzzz-999").getString());
151 }
152
TEST_F(FontLanguageTest,ScriptEqualTest)153 TEST_F(FontLanguageTest, ScriptEqualTest) {
154 EXPECT_TRUE(createFontLanguage("en").isEqualScript(createFontLanguage("en")));
155 EXPECT_TRUE(createFontLanguage("en-Latn").isEqualScript(createFontLanguage("en")));
156 EXPECT_TRUE(createFontLanguage("jp-Latn").isEqualScript(createFontLanguage("en-Latn")));
157 EXPECT_TRUE(createFontLanguage("en-Jpan").isEqualScript(createFontLanguage("en-Jpan")));
158
159 EXPECT_FALSE(createFontLanguage("en-Jpan").isEqualScript(createFontLanguage("en-Hira")));
160 EXPECT_FALSE(createFontLanguage("en-Jpan").isEqualScript(createFontLanguage("en-Hani")));
161 }
162
TEST_F(FontLanguageTest,ScriptMatchTest)163 TEST_F(FontLanguageTest, ScriptMatchTest) {
164 const bool SUPPORTED = true;
165 const bool NOT_SUPPORTED = false;
166
167 struct TestCase {
168 const std::string baseScript;
169 const std::string requestedScript;
170 bool isSupported;
171 } testCases[] = {
172 // Same scripts
173 { "en-Latn", "Latn", SUPPORTED },
174 { "ja-Jpan", "Jpan", SUPPORTED },
175 { "ja-Hira", "Hira", SUPPORTED },
176 { "ja-Kana", "Kana", SUPPORTED },
177 { "ja-Hrkt", "Hrkt", SUPPORTED },
178 { "zh-Hans", "Hans", SUPPORTED },
179 { "zh-Hant", "Hant", SUPPORTED },
180 { "zh-Hani", "Hani", SUPPORTED },
181 { "ko-Kore", "Kore", SUPPORTED },
182 { "ko-Hang", "Hang", SUPPORTED },
183 { "zh-Hanb", "Hanb", SUPPORTED },
184
185 // Japanese supports Hiragana, Katakanara, etc.
186 { "ja-Jpan", "Hira", SUPPORTED },
187 { "ja-Jpan", "Kana", SUPPORTED },
188 { "ja-Jpan", "Hrkt", SUPPORTED },
189 { "ja-Hrkt", "Hira", SUPPORTED },
190 { "ja-Hrkt", "Kana", SUPPORTED },
191
192 // Chinese supports Han.
193 { "zh-Hans", "Hani", SUPPORTED },
194 { "zh-Hant", "Hani", SUPPORTED },
195 { "zh-Hanb", "Hani", SUPPORTED },
196
197 // Hanb supports Bopomofo.
198 { "zh-Hanb", "Bopo", SUPPORTED },
199
200 // Korean supports Hangul.
201 { "ko-Kore", "Hang", SUPPORTED },
202
203 // Different scripts
204 { "ja-Jpan", "Latn", NOT_SUPPORTED },
205 { "en-Latn", "Jpan", NOT_SUPPORTED },
206 { "ja-Jpan", "Hant", NOT_SUPPORTED },
207 { "zh-Hant", "Jpan", NOT_SUPPORTED },
208 { "ja-Jpan", "Hans", NOT_SUPPORTED },
209 { "zh-Hans", "Jpan", NOT_SUPPORTED },
210 { "ja-Jpan", "Kore", NOT_SUPPORTED },
211 { "ko-Kore", "Jpan", NOT_SUPPORTED },
212 { "zh-Hans", "Hant", NOT_SUPPORTED },
213 { "zh-Hant", "Hans", NOT_SUPPORTED },
214 { "zh-Hans", "Kore", NOT_SUPPORTED },
215 { "ko-Kore", "Hans", NOT_SUPPORTED },
216 { "zh-Hant", "Kore", NOT_SUPPORTED },
217 { "ko-Kore", "Hant", NOT_SUPPORTED },
218
219 // Hiragana doesn't support Japanese, etc.
220 { "ja-Hira", "Jpan", NOT_SUPPORTED },
221 { "ja-Kana", "Jpan", NOT_SUPPORTED },
222 { "ja-Hrkt", "Jpan", NOT_SUPPORTED },
223 { "ja-Hani", "Jpan", NOT_SUPPORTED },
224 { "ja-Hira", "Hrkt", NOT_SUPPORTED },
225 { "ja-Kana", "Hrkt", NOT_SUPPORTED },
226 { "ja-Hani", "Hrkt", NOT_SUPPORTED },
227 { "ja-Hani", "Hira", NOT_SUPPORTED },
228 { "ja-Hani", "Kana", NOT_SUPPORTED },
229
230 // Kanji doesn't support Chinese, etc.
231 { "zh-Hani", "Hant", NOT_SUPPORTED },
232 { "zh-Hani", "Hans", NOT_SUPPORTED },
233 { "zh-Hani", "Hanb", NOT_SUPPORTED },
234
235 // Hangul doesn't support Korean, etc.
236 { "ko-Hang", "Kore", NOT_SUPPORTED },
237 { "ko-Hani", "Kore", NOT_SUPPORTED },
238 { "ko-Hani", "Hang", NOT_SUPPORTED },
239 { "ko-Hang", "Hani", NOT_SUPPORTED },
240
241 // Han with botomofo doesn't support simplified Chinese, etc.
242 { "zh-Hanb", "Hant", NOT_SUPPORTED },
243 { "zh-Hanb", "Hans", NOT_SUPPORTED },
244 { "zh-Hanb", "Jpan", NOT_SUPPORTED },
245 { "zh-Hanb", "Kore", NOT_SUPPORTED },
246 };
247
248 for (auto testCase : testCases) {
249 hb_script_t script = hb_script_from_iso15924_tag(
250 HB_TAG(testCase.requestedScript[0], testCase.requestedScript[1],
251 testCase.requestedScript[2], testCase.requestedScript[3]));
252 if (testCase.isSupported) {
253 EXPECT_TRUE(
254 createFontLanguage(testCase.baseScript).supportsHbScript(script))
255 << testCase.baseScript << " should support " << testCase.requestedScript;
256 } else {
257 EXPECT_FALSE(
258 createFontLanguage(testCase.baseScript).supportsHbScript(script))
259 << testCase.baseScript << " shouldn't support " << testCase.requestedScript;
260 }
261 }
262 }
263
TEST_F(FontLanguagesTest,basicTests)264 TEST_F(FontLanguagesTest, basicTests) {
265 FontLanguages emptyLangs;
266 EXPECT_EQ(0u, emptyLangs.size());
267
268 FontLanguage english = createFontLanguage("en");
269 const FontLanguages& singletonLangs = createFontLanguages("en");
270 EXPECT_EQ(1u, singletonLangs.size());
271 EXPECT_EQ(english, singletonLangs[0]);
272
273 FontLanguage french = createFontLanguage("fr");
274 const FontLanguages& twoLangs = createFontLanguages("en,fr");
275 EXPECT_EQ(2u, twoLangs.size());
276 EXPECT_EQ(english, twoLangs[0]);
277 EXPECT_EQ(french, twoLangs[1]);
278 }
279
TEST_F(FontLanguagesTest,unsupportedLanguageTests)280 TEST_F(FontLanguagesTest, unsupportedLanguageTests) {
281 const FontLanguages& oneUnsupported = createFontLanguages("abcd-example");
282 EXPECT_TRUE(oneUnsupported.empty());
283
284 const FontLanguages& twoUnsupporteds = createFontLanguages("abcd-example,abcd-example");
285 EXPECT_TRUE(twoUnsupporteds.empty());
286
287 FontLanguage english = createFontLanguage("en");
288 const FontLanguages& firstUnsupported = createFontLanguages("abcd-example,en");
289 EXPECT_EQ(1u, firstUnsupported.size());
290 EXPECT_EQ(english, firstUnsupported[0]);
291
292 const FontLanguages& lastUnsupported = createFontLanguages("en,abcd-example");
293 EXPECT_EQ(1u, lastUnsupported.size());
294 EXPECT_EQ(english, lastUnsupported[0]);
295 }
296
TEST_F(FontLanguagesTest,repeatedLanguageTests)297 TEST_F(FontLanguagesTest, repeatedLanguageTests) {
298 FontLanguage english = createFontLanguage("en");
299 FontLanguage french = createFontLanguage("fr");
300 FontLanguage canadianFrench = createFontLanguage("fr-CA");
301 FontLanguage englishInLatn = createFontLanguage("en-Latn");
302 ASSERT_TRUE(english == englishInLatn);
303
304 const FontLanguages& langs = createFontLanguages("en,en-Latn");
305 EXPECT_EQ(1u, langs.size());
306 EXPECT_EQ(english, langs[0]);
307
308 const FontLanguages& fr = createFontLanguages("fr,fr-FR,fr-Latn-FR");
309 EXPECT_EQ(1u, fr.size());
310 EXPECT_EQ(french, fr[0]);
311
312 // ICU appends FR to fr. The third language is dropped which is same as the first language.
313 const FontLanguages& fr2 = createFontLanguages("fr,fr-CA,fr-FR");
314 EXPECT_EQ(2u, fr2.size());
315 EXPECT_EQ(french, fr2[0]);
316 EXPECT_EQ(canadianFrench, fr2[1]);
317
318 // The order should be kept.
319 const FontLanguages& langs2 = createFontLanguages("en,fr,en-Latn");
320 EXPECT_EQ(2u, langs2.size());
321 EXPECT_EQ(english, langs2[0]);
322 EXPECT_EQ(french, langs2[1]);
323 }
324
TEST_F(FontLanguagesTest,identifierTest)325 TEST_F(FontLanguagesTest, identifierTest) {
326 EXPECT_EQ(createFontLanguage("en-Latn-US"), createFontLanguage("en-Latn-US"));
327 EXPECT_EQ(createFontLanguage("zh-Hans-CN"), createFontLanguage("zh-Hans-CN"));
328 EXPECT_EQ(createFontLanguage("en-Zsye-US"), createFontLanguage("en-Zsye-US"));
329
330 EXPECT_NE(createFontLanguage("en-Latn-US"), createFontLanguage("en-Latn-GB"));
331 EXPECT_NE(createFontLanguage("en-Latn-US"), createFontLanguage("en-Zsye-US"));
332 EXPECT_NE(createFontLanguage("es-Latn-US"), createFontLanguage("en-Latn-US"));
333 EXPECT_NE(createFontLanguage("zh-Hant-HK"), createFontLanguage("zh-Hant-TW"));
334 }
335
TEST_F(FontLanguagesTest,undEmojiTests)336 TEST_F(FontLanguagesTest, undEmojiTests) {
337 FontLanguage emoji = createFontLanguage("und-Zsye");
338 EXPECT_EQ(FontLanguage::EMSTYLE_EMOJI, emoji.getEmojiStyle());
339
340 FontLanguage und = createFontLanguage("und");
341 EXPECT_EQ(FontLanguage::EMSTYLE_EMPTY, und.getEmojiStyle());
342 EXPECT_FALSE(emoji == und);
343
344 FontLanguage undExample = createFontLanguage("und-example");
345 EXPECT_EQ(FontLanguage::EMSTYLE_EMPTY, undExample.getEmojiStyle());
346 EXPECT_FALSE(emoji == undExample);
347 }
348
TEST_F(FontLanguagesTest,subtagEmojiTest)349 TEST_F(FontLanguagesTest, subtagEmojiTest) {
350 std::string subtagEmojiStrings[] = {
351 // Duplicate subtag case.
352 "und-Latn-u-em-emoji-u-em-text",
353
354 // Strings that contain language.
355 "und-u-em-emoji",
356 "en-u-em-emoji",
357
358 // Strings that contain the script.
359 "und-Jpan-u-em-emoji",
360 "en-Latn-u-em-emoji",
361 "und-Zsym-u-em-emoji",
362 "und-Zsye-u-em-emoji",
363 "en-Zsym-u-em-emoji",
364 "en-Zsye-u-em-emoji",
365
366 // Strings that contain the county.
367 "und-US-u-em-emoji",
368 "en-US-u-em-emoji",
369 "es-419-u-em-emoji",
370 "und-Latn-US-u-em-emoji",
371 "en-Zsym-US-u-em-emoji",
372 "en-Zsye-US-u-em-emoji",
373 "es-Zsye-419-u-em-emoji",
374 };
375
376 for (auto subtagEmojiString : subtagEmojiStrings) {
377 SCOPED_TRACE("Test for \"" + subtagEmojiString + "\"");
378 FontLanguage subtagEmoji = createFontLanguage(subtagEmojiString);
379 EXPECT_EQ(FontLanguage::EMSTYLE_EMOJI, subtagEmoji.getEmojiStyle());
380 }
381 }
382
TEST_F(FontLanguagesTest,subtagTextTest)383 TEST_F(FontLanguagesTest, subtagTextTest) {
384 std::string subtagTextStrings[] = {
385 // Duplicate subtag case.
386 "und-Latn-u-em-text-u-em-emoji",
387
388 // Strings that contain language.
389 "und-u-em-text",
390 "en-u-em-text",
391
392 // Strings that contain the script.
393 "und-Latn-u-em-text",
394 "en-Jpan-u-em-text",
395 "und-Zsym-u-em-text",
396 "und-Zsye-u-em-text",
397 "en-Zsym-u-em-text",
398 "en-Zsye-u-em-text",
399
400 // Strings that contain the county.
401 "und-US-u-em-text",
402 "en-US-u-em-text",
403 "es-419-u-em-text",
404 "und-Latn-US-u-em-text",
405 "en-Zsym-US-u-em-text",
406 "en-Zsye-US-u-em-text",
407 "es-Zsye-419-u-em-text",
408 };
409
410 for (auto subtagTextString : subtagTextStrings) {
411 SCOPED_TRACE("Test for \"" + subtagTextString + "\"");
412 FontLanguage subtagText = createFontLanguage(subtagTextString);
413 EXPECT_EQ(FontLanguage::EMSTYLE_TEXT, subtagText.getEmojiStyle());
414 }
415 }
416
417 // TODO: add more "und" language cases whose language and script are
418 // unexpectedly translated to en-Latn by ICU.
TEST_F(FontLanguagesTest,subtagDefaultTest)419 TEST_F(FontLanguagesTest, subtagDefaultTest) {
420 std::string subtagDefaultStrings[] = {
421 // Duplicate subtag case.
422 "en-Latn-u-em-default-u-em-emoji",
423 "en-Latn-u-em-default-u-em-text",
424
425 // Strings that contain language.
426 "und-u-em-default",
427 "en-u-em-default",
428
429 // Strings that contain the script.
430 "en-Latn-u-em-default",
431 "en-Zsym-u-em-default",
432 "en-Zsye-u-em-default",
433
434 // Strings that contain the county.
435 "en-US-u-em-default",
436 "en-Latn-US-u-em-default",
437 "es-Latn-419-u-em-default",
438 "en-Zsym-US-u-em-default",
439 "en-Zsye-US-u-em-default",
440 "es-Zsye-419-u-em-default",
441 };
442
443 for (auto subtagDefaultString : subtagDefaultStrings) {
444 SCOPED_TRACE("Test for \"" + subtagDefaultString + "\"");
445 FontLanguage subtagDefault = createFontLanguage(subtagDefaultString);
446 EXPECT_EQ(FontLanguage::EMSTYLE_DEFAULT, subtagDefault.getEmojiStyle());
447 }
448 }
449
TEST_F(FontLanguagesTest,subtagEmptyTest)450 TEST_F(FontLanguagesTest, subtagEmptyTest) {
451 std::string subtagEmptyStrings[] = {
452 "und",
453 "jp",
454 "en-US",
455 "en-Latn",
456 "en-Latn-US",
457 "en-Latn-US-u-em",
458 "en-Latn-US-u-em-defaultemoji",
459 };
460
461 for (auto subtagEmptyString : subtagEmptyStrings) {
462 SCOPED_TRACE("Test for \"" + subtagEmptyString + "\"");
463 FontLanguage subtagEmpty = createFontLanguage(subtagEmptyString);
464 EXPECT_EQ(FontLanguage::EMSTYLE_EMPTY, subtagEmpty.getEmojiStyle());
465 }
466 }
467
TEST_F(FontLanguagesTest,registerLanguageListTest)468 TEST_F(FontLanguagesTest, registerLanguageListTest) {
469 EXPECT_EQ(0UL, FontStyle::registerLanguageList(""));
470 EXPECT_NE(0UL, FontStyle::registerLanguageList("en"));
471 EXPECT_NE(0UL, FontStyle::registerLanguageList("jp"));
472 EXPECT_NE(0UL, FontStyle::registerLanguageList("en,zh-Hans"));
473
474 EXPECT_EQ(FontStyle::registerLanguageList("en"), FontStyle::registerLanguageList("en"));
475 EXPECT_NE(FontStyle::registerLanguageList("en"), FontStyle::registerLanguageList("jp"));
476
477 EXPECT_EQ(FontStyle::registerLanguageList("en,zh-Hans"),
478 FontStyle::registerLanguageList("en,zh-Hans"));
479 EXPECT_NE(FontStyle::registerLanguageList("en,zh-Hans"),
480 FontStyle::registerLanguageList("zh-Hans,en"));
481 EXPECT_NE(FontStyle::registerLanguageList("en,zh-Hans"),
482 FontStyle::registerLanguageList("jp"));
483 EXPECT_NE(FontStyle::registerLanguageList("en,zh-Hans"),
484 FontStyle::registerLanguageList("en"));
485 EXPECT_NE(FontStyle::registerLanguageList("en,zh-Hans"),
486 FontStyle::registerLanguageList("en,zh-Hant"));
487 }
488
489 // The test font has following glyphs.
490 // U+82A6
491 // U+82A6 U+FE00 (VS1)
492 // U+82A6 U+E0100 (VS17)
493 // U+82A6 U+E0101 (VS18)
494 // U+82A6 U+E0102 (VS19)
495 // U+845B
496 // U+845B U+FE00 (VS2)
497 // U+845B U+E0101 (VS18)
498 // U+845B U+E0102 (VS19)
499 // U+845B U+E0103 (VS20)
500 // U+537F
501 // U+717D U+FE02 (VS3)
502 // U+717D U+E0102 (VS19)
503 // U+717D U+E0103 (VS20)
504 const char kVsTestFont[] = kTestFontDir "VariationSelectorTest-Regular.ttf";
505
506 class FontFamilyTest : public ICUTestBase {
507 public:
SetUp()508 virtual void SetUp() override {
509 ICUTestBase::SetUp();
510 if (access(kVsTestFont, R_OK) != 0) {
511 FAIL() << "Unable to read " << kVsTestFont << ". "
512 << "Please prepare the test data directory. "
513 << "For more details, please see how_to_run.txt.";
514 }
515 }
516 };
517
518 // Asserts that the font family has glyphs for and only for specified codepoint
519 // and variationSelector pairs.
expectVSGlyphs(FontFamily * family,uint32_t codepoint,const std::set<uint32_t> & vs)520 void expectVSGlyphs(FontFamily* family, uint32_t codepoint, const std::set<uint32_t>& vs) {
521 for (uint32_t i = 0xFE00; i <= 0xE01EF; ++i) {
522 // Move to variation selectors supplements after variation selectors.
523 if (i == 0xFF00) {
524 i = 0xE0100;
525 }
526 if (vs.find(i) == vs.end()) {
527 EXPECT_FALSE(family->hasGlyph(codepoint, i))
528 << "Glyph for U+" << std::hex << codepoint << " U+" << i;
529 } else {
530 EXPECT_TRUE(family->hasGlyph(codepoint, i))
531 << "Glyph for U+" << std::hex << codepoint << " U+" << i;
532 }
533
534 }
535 }
536
TEST_F(FontFamilyTest,hasVariationSelectorTest)537 TEST_F(FontFamilyTest, hasVariationSelectorTest) {
538 std::shared_ptr<MinikinFont> minikinFont(new MinikinFontForTest(kVsTestFont));
539 std::shared_ptr<FontFamily> family(
540 new FontFamily(std::vector<Font>{ Font(minikinFont, FontStyle()) }));
541
542 const uint32_t kVS1 = 0xFE00;
543 const uint32_t kVS2 = 0xFE01;
544 const uint32_t kVS3 = 0xFE02;
545 const uint32_t kVS17 = 0xE0100;
546 const uint32_t kVS18 = 0xE0101;
547 const uint32_t kVS19 = 0xE0102;
548 const uint32_t kVS20 = 0xE0103;
549
550 const uint32_t kSupportedChar1 = 0x82A6;
551 EXPECT_TRUE(family->getCoverage().get(kSupportedChar1));
552 expectVSGlyphs(family.get(), kSupportedChar1, std::set<uint32_t>({kVS1, kVS17, kVS18, kVS19}));
553
554 const uint32_t kSupportedChar2 = 0x845B;
555 EXPECT_TRUE(family->getCoverage().get(kSupportedChar2));
556 expectVSGlyphs(family.get(), kSupportedChar2, std::set<uint32_t>({kVS2, kVS18, kVS19, kVS20}));
557
558 const uint32_t kNoVsSupportedChar = 0x537F;
559 EXPECT_TRUE(family->getCoverage().get(kNoVsSupportedChar));
560 expectVSGlyphs(family.get(), kNoVsSupportedChar, std::set<uint32_t>());
561
562 const uint32_t kVsOnlySupportedChar = 0x717D;
563 EXPECT_FALSE(family->getCoverage().get(kVsOnlySupportedChar));
564 expectVSGlyphs(family.get(), kVsOnlySupportedChar, std::set<uint32_t>({kVS3, kVS19, kVS20}));
565
566 const uint32_t kNotSupportedChar = 0x845C;
567 EXPECT_FALSE(family->getCoverage().get(kNotSupportedChar));
568 expectVSGlyphs(family.get(), kNotSupportedChar, std::set<uint32_t>());
569 }
570
TEST_F(FontFamilyTest,hasVSTableTest)571 TEST_F(FontFamilyTest, hasVSTableTest) {
572 struct TestCase {
573 const std::string fontPath;
574 bool hasVSTable;
575 } testCases[] = {
576 { kTestFontDir "Ja.ttf", true },
577 { kTestFontDir "ZhHant.ttf", true },
578 { kTestFontDir "ZhHans.ttf", true },
579 { kTestFontDir "Italic.ttf", false },
580 { kTestFontDir "Bold.ttf", false },
581 { kTestFontDir "BoldItalic.ttf", false },
582 };
583
584 for (auto testCase : testCases) {
585 SCOPED_TRACE(testCase.hasVSTable ?
586 "Font " + testCase.fontPath + " should have a variation sequence table." :
587 "Font " + testCase.fontPath + " shouldn't have a variation sequence table.");
588
589 std::shared_ptr<MinikinFont> minikinFont(
590 new MinikinFontForTest(testCase.fontPath));
591 std::shared_ptr<FontFamily> family(new FontFamily(
592 std::vector<Font>{ Font(minikinFont, FontStyle()) }));
593 EXPECT_EQ(testCase.hasVSTable, family->hasVSTable());
594 }
595 }
596
TEST_F(FontFamilyTest,createFamilyWithVariationTest)597 TEST_F(FontFamilyTest, createFamilyWithVariationTest) {
598 // This font has 'wdth' and 'wght' axes.
599 const char kMultiAxisFont[] = kTestFontDir "/MultiAxis.ttf";
600 const char kNoAxisFont[] = kTestFontDir "/Regular.ttf";
601
602 std::shared_ptr<FontFamily> multiAxisFamily = makeFamily(kMultiAxisFont);
603 std::shared_ptr<FontFamily> noAxisFamily = makeFamily(kNoAxisFont);
604
605 {
606 // Do not ceate new instance if none of variations are specified.
607 EXPECT_EQ(nullptr,
608 multiAxisFamily->createFamilyWithVariation(std::vector<FontVariation>()));
609 EXPECT_EQ(nullptr,
610 noAxisFamily->createFamilyWithVariation(std::vector<FontVariation>()));
611 }
612 {
613 // New instance should be used for supported variation.
614 std::vector<FontVariation> variations = {{MinikinFont::MakeTag('w', 'd', 't', 'h'), 1.0f}};
615 std::shared_ptr<FontFamily> newFamily(
616 multiAxisFamily->createFamilyWithVariation(variations));
617 EXPECT_NE(nullptr, newFamily.get());
618 EXPECT_NE(multiAxisFamily.get(), newFamily.get());
619 EXPECT_EQ(nullptr, noAxisFamily->createFamilyWithVariation(variations));
620 }
621 {
622 // New instance should be used for supported variation. (multiple variations case)
623 std::vector<FontVariation> variations = {
624 { MinikinFont::MakeTag('w', 'd', 't', 'h'), 1.0f },
625 { MinikinFont::MakeTag('w', 'g', 'h', 't'), 1.0f }
626 };
627 std::shared_ptr<FontFamily> newFamily(
628 multiAxisFamily->createFamilyWithVariation(variations));
629 EXPECT_NE(nullptr, newFamily.get());
630 EXPECT_NE(multiAxisFamily.get(), newFamily.get());
631 EXPECT_EQ(nullptr, noAxisFamily->createFamilyWithVariation(variations));
632 }
633 {
634 // Do not ceate new instance if none of variations are supported.
635 std::vector<FontVariation> variations = {
636 { MinikinFont::MakeTag('Z', 'Z', 'Z', 'Z'), 1.0f }
637 };
638 EXPECT_EQ(nullptr, multiAxisFamily->createFamilyWithVariation(variations));
639 EXPECT_EQ(nullptr, noAxisFamily->createFamilyWithVariation(variations));
640 }
641 {
642 // At least one axis is supported, should create new instance.
643 std::vector<FontVariation> variations = {
644 { MinikinFont::MakeTag('w', 'd', 't', 'h'), 1.0f },
645 { MinikinFont::MakeTag('Z', 'Z', 'Z', 'Z'), 1.0f }
646 };
647 std::shared_ptr<FontFamily> newFamily(
648 multiAxisFamily->createFamilyWithVariation(variations));
649 EXPECT_NE(nullptr, newFamily.get());
650 EXPECT_NE(multiAxisFamily.get(), newFamily.get());
651 EXPECT_EQ(nullptr, noAxisFamily->createFamilyWithVariation(variations));
652 }
653 }
654
TEST_F(FontFamilyTest,coverageTableSelectionTest)655 TEST_F(FontFamilyTest, coverageTableSelectionTest) {
656 // This font supports U+0061. The cmap subtable is format 4 and its platform ID is 0 and
657 // encoding ID is 1.
658 const char kUnicodeEncoding1Font[] = kTestFontDir "UnicodeBMPOnly.ttf";
659
660 // This font supports U+0061. The cmap subtable is format 4 and its platform ID is 0 and
661 // encoding ID is 3.
662 const char kUnicodeEncoding3Font[] = kTestFontDir "UnicodeBMPOnly2.ttf";
663
664 // This font has both cmap format 4 subtable which platform ID is 0 and encoding ID is 1
665 // and cmap format 14 subtable which platform ID is 0 and encoding ID is 10.
666 // U+0061 is listed in both subtable but U+1F926 is only listed in latter.
667 const char kUnicodeEncoding4Font[] = kTestFontDir "UnicodeUCS4.ttf";
668
669 std::shared_ptr<FontFamily> unicodeEnc1Font = makeFamily(kUnicodeEncoding1Font);
670 std::shared_ptr<FontFamily> unicodeEnc3Font = makeFamily(kUnicodeEncoding3Font);
671 std::shared_ptr<FontFamily> unicodeEnc4Font = makeFamily(kUnicodeEncoding4Font);
672
673 android::AutoMutex _l(gMinikinLock);
674
675 EXPECT_TRUE(unicodeEnc1Font->hasGlyph(0x0061, 0));
676 EXPECT_TRUE(unicodeEnc3Font->hasGlyph(0x0061, 0));
677 EXPECT_TRUE(unicodeEnc4Font->hasGlyph(0x0061, 0));
678
679 EXPECT_TRUE(unicodeEnc4Font->hasGlyph(0x1F926, 0));
680 }
681
682 } // namespace minikin
683