FontMgrAndroidParserTest.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. /*
  2. * Copyright 2014 Google Inc.
  3. *
  4. * Use of this source code is governed by a BSD-style license that can be
  5. * found in the LICENSE file.
  6. */
  7. #include "tests/Test.h"
  8. #include "include/core/SkCanvas.h"
  9. #include "include/core/SkFont.h"
  10. #include "include/core/SkTypeface.h"
  11. #include "include/ports/SkFontMgr_android.h"
  12. #include "include/private/SkFixed.h"
  13. #include "src/core/SkOSFile.h"
  14. #include "src/ports/SkFontMgr_android_parser.h"
  15. #include "tools/Resources.h"
  16. #include "tools/flags/CommandLineFlags.h"
  17. #include <cmath>
  18. #include <cstdio>
  19. DECLARE_bool(verboseFontMgr);
  20. int CountFallbacks(SkTDArray<FontFamily*> fontFamilies) {
  21. int countOfFallbackFonts = 0;
  22. for (int i = 0; i < fontFamilies.count(); i++) {
  23. if (fontFamilies[i]->fIsFallbackFont) {
  24. countOfFallbackFonts++;
  25. }
  26. }
  27. return countOfFallbackFonts;
  28. }
  29. //https://tools.ietf.org/html/rfc5234#appendix-B.1
  30. static bool isALPHA(int c) {
  31. return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
  32. }
  33. //https://tools.ietf.org/html/rfc5234#appendix-B.1
  34. static bool isDIGIT(int c) {
  35. return ('0' <= c && c <= '9');
  36. }
  37. static void ValidateLoadedFonts(SkTDArray<FontFamily*> fontFamilies, const char* firstExpectedFile,
  38. skiatest::Reporter* reporter) {
  39. REPORTER_ASSERT(reporter, fontFamilies[0]->fNames.count() == 5);
  40. REPORTER_ASSERT(reporter, !strcmp(fontFamilies[0]->fNames[0].c_str(), "sans-serif"));
  41. REPORTER_ASSERT(reporter,
  42. !strcmp(fontFamilies[0]->fFonts[0].fFileName.c_str(), firstExpectedFile));
  43. REPORTER_ASSERT(reporter, !fontFamilies[0]->fIsFallbackFont);
  44. // Check that the languages are all sane.
  45. for (const auto& fontFamily : fontFamilies) {
  46. for (const auto& lang : fontFamily->fLanguages) {
  47. const SkString& langString = lang.getTag();
  48. for (size_t i = 0; i < langString.size(); ++i) {
  49. int c = langString[i];
  50. REPORTER_ASSERT(reporter, isALPHA(c) || isDIGIT(c) || '-' == c);
  51. }
  52. }
  53. }
  54. // All file names in the test configuration files start with a capital letter.
  55. // This is not a general requirement, but it is true of all the test configuration data.
  56. // Verifying ensures the filenames have been read sanely and have not been 'sliced'.
  57. for (int i = 0; i < fontFamilies.count(); ++i) {
  58. FontFamily& family = *fontFamilies[i];
  59. for (int j = 0; j < family.fFonts.count(); ++j) {
  60. FontFileInfo& file = family.fFonts[j];
  61. REPORTER_ASSERT(reporter, !file.fFileName.isEmpty() &&
  62. file.fFileName[0] >= 'A' &&
  63. file.fFileName[0] <= 'Z');
  64. }
  65. }
  66. }
  67. static void DumpFiles(const FontFamily& fontFamily) {
  68. for (int j = 0; j < fontFamily.fFonts.count(); ++j) {
  69. const FontFileInfo& ffi = fontFamily.fFonts[j];
  70. SkDebugf(" file (%d) %s#%d", ffi.fWeight, ffi.fFileName.c_str(), ffi.fIndex);
  71. for (const auto& coordinate : ffi.fVariationDesignPosition) {
  72. SkDebugf(" @'%c%c%c%c'=%f",
  73. (coordinate.axis >> 24) & 0xFF,
  74. (coordinate.axis >> 16) & 0xFF,
  75. (coordinate.axis >> 8) & 0xFF,
  76. (coordinate.axis ) & 0xFF,
  77. coordinate.value);
  78. }
  79. SkDebugf("\n");
  80. }
  81. }
  82. static void DumpLoadedFonts(SkTDArray<FontFamily*> fontFamilies, const char* label) {
  83. if (!FLAGS_verboseFontMgr) {
  84. return;
  85. }
  86. SkDebugf("\n--- Dumping %s\n", label);
  87. for (int i = 0; i < fontFamilies.count(); ++i) {
  88. SkDebugf("Family %d:\n", i);
  89. switch(fontFamilies[i]->fVariant) {
  90. case kElegant_FontVariant: SkDebugf(" elegant\n"); break;
  91. case kCompact_FontVariant: SkDebugf(" compact\n"); break;
  92. default: break;
  93. }
  94. SkDebugf(" basePath %s\n", fontFamilies[i]->fBasePath.c_str());
  95. if (!fontFamilies[i]->fLanguages.empty()) {
  96. SkDebugf(" language");
  97. for (const auto& lang : fontFamilies[i]->fLanguages) {
  98. SkDebugf(" %s", lang.getTag().c_str());
  99. }
  100. SkDebugf("\n");
  101. }
  102. for (int j = 0; j < fontFamilies[i]->fNames.count(); ++j) {
  103. SkDebugf(" name %s\n", fontFamilies[i]->fNames[j].c_str());
  104. }
  105. DumpFiles(*fontFamilies[i]);
  106. fontFamilies[i]->fallbackFamilies.foreach(
  107. [](SkString, std::unique_ptr<FontFamily>* fallbackFamily) {
  108. SkDebugf(" Fallback for: %s\n", (*fallbackFamily)->fFallbackFor.c_str());
  109. DumpFiles(*(*fallbackFamily).get());
  110. }
  111. );
  112. }
  113. SkDebugf("\n\n");
  114. }
  115. template <int N, typename T> static double test_parse_fixed_r(skiatest::Reporter* reporter,
  116. double low, double high, double inc)
  117. {
  118. double SK_FixedMax_double = nextafter(1 << (sizeof(T) * CHAR_BIT - N - 1), 0.0);
  119. double SK_FixedEpsilon_double = (1.0 / (1 << N));
  120. double maxError = 0;
  121. char buffer[64];
  122. for (double f = low; f < high; f += inc) {
  123. SkString s;
  124. // 'sprintf' formatting as expected depends on the current locale being "C".
  125. // We currently expect tests and tools to run in the "C" locale.
  126. sprintf(buffer, "%.20f", f);
  127. T fix;
  128. bool b = parse_fixed<N>(buffer, &fix);
  129. if (b) {
  130. double f2 = fix * SK_FixedEpsilon_double;
  131. double error = fabs(f - f2);
  132. REPORTER_ASSERT(reporter, error <= SK_FixedEpsilon_double);
  133. maxError = SkTMax(maxError, error);
  134. } else {
  135. REPORTER_ASSERT(reporter, f < -SK_FixedMax_double || SK_FixedMax_double < f);
  136. }
  137. }
  138. //SkDebugf("maxError: %.20f\n", maxError);
  139. return maxError;
  140. }
  141. static void test_parse_fixed(skiatest::Reporter* reporter) {
  142. test_parse_fixed_r<27, int32_t>(reporter, -8.1, -7.9, 0.000001);
  143. test_parse_fixed_r<27, int32_t>(reporter, -0.1, 0.1, 0.000001);
  144. test_parse_fixed_r<27, int32_t>(reporter, 7.9, 8.1, 0.000001);
  145. test_parse_fixed_r<16, int32_t>(reporter, -0.125, 0.125, 1.0 / (1 << 19));
  146. test_parse_fixed_r<16, int32_t>(reporter, -32768.125, -32766.875, 1.0 / (1 << 17));
  147. test_parse_fixed_r<16, int32_t>(reporter, 32766.875, 32768.125, 1.0 / (1 << 17));
  148. test_parse_fixed_r<16, int32_t>(reporter, -1.1, 1.1, 0.0001);
  149. SkFixed fix;
  150. REPORTER_ASSERT(reporter, !parse_fixed<27>("-17.1", &fix));
  151. REPORTER_ASSERT(reporter, !parse_fixed<16>("32768", &fix));
  152. REPORTER_ASSERT(reporter, !parse_fixed<16>("", &fix));
  153. REPORTER_ASSERT(reporter, !parse_fixed<16>(".", &fix));
  154. REPORTER_ASSERT(reporter, !parse_fixed<16>("123.", &fix));
  155. REPORTER_ASSERT(reporter, !parse_fixed<16>("a", &fix));
  156. REPORTER_ASSERT(reporter, !parse_fixed<16>(".123a", &fix));
  157. }
  158. DEF_TEST(FontMgrAndroidParser, reporter) {
  159. test_parse_fixed(reporter);
  160. bool resourcesMissing = false;
  161. SkTDArray<FontFamily*> preV17FontFamilies;
  162. SkFontMgr_Android_Parser::GetCustomFontFamilies(preV17FontFamilies,
  163. SkString("/custom/font/path/"),
  164. GetResourcePath("android_fonts/pre_v17/system_fonts.xml").c_str(),
  165. GetResourcePath("android_fonts/pre_v17/fallback_fonts.xml").c_str());
  166. if (preV17FontFamilies.count() > 0) {
  167. REPORTER_ASSERT(reporter, preV17FontFamilies.count() == 14);
  168. REPORTER_ASSERT(reporter, CountFallbacks(preV17FontFamilies) == 10);
  169. DumpLoadedFonts(preV17FontFamilies, "pre version 17");
  170. ValidateLoadedFonts(preV17FontFamilies, "Roboto-Regular.ttf", reporter);
  171. } else {
  172. resourcesMissing = true;
  173. }
  174. preV17FontFamilies.deleteAll();
  175. SkTDArray<FontFamily*> v17FontFamilies;
  176. SkFontMgr_Android_Parser::GetCustomFontFamilies(v17FontFamilies,
  177. SkString("/custom/font/path/"),
  178. GetResourcePath("android_fonts/v17/system_fonts.xml").c_str(),
  179. GetResourcePath("android_fonts/v17/fallback_fonts.xml").c_str(),
  180. GetResourcePath("android_fonts/v17").c_str());
  181. if (v17FontFamilies.count() > 0) {
  182. REPORTER_ASSERT(reporter, v17FontFamilies.count() == 56);
  183. REPORTER_ASSERT(reporter, CountFallbacks(v17FontFamilies) == 46);
  184. DumpLoadedFonts(v17FontFamilies, "version 17");
  185. ValidateLoadedFonts(v17FontFamilies, "Roboto-Regular.ttf", reporter);
  186. } else {
  187. resourcesMissing = true;
  188. }
  189. v17FontFamilies.deleteAll();
  190. SkTDArray<FontFamily*> v22FontFamilies;
  191. SkFontMgr_Android_Parser::GetCustomFontFamilies(v22FontFamilies,
  192. SkString("/custom/font/path/"),
  193. GetResourcePath("android_fonts/v22/fonts.xml").c_str(),
  194. nullptr);
  195. if (v22FontFamilies.count() > 0) {
  196. REPORTER_ASSERT(reporter, v22FontFamilies.count() == 54);
  197. REPORTER_ASSERT(reporter, CountFallbacks(v22FontFamilies) == 42);
  198. DumpLoadedFonts(v22FontFamilies, "version 22");
  199. ValidateLoadedFonts(v22FontFamilies, "Roboto-Thin.ttf", reporter);
  200. } else {
  201. resourcesMissing = true;
  202. }
  203. v22FontFamilies.deleteAll();
  204. if (resourcesMissing) {
  205. SkDebugf("---- Resource files missing for FontConfigParser test\n");
  206. }
  207. }
  208. DEF_TEST(FontMgrAndroidLegacyMakeTypeface, reporter) {
  209. constexpr char fontsXmlFilename[] = "fonts/fonts.xml";
  210. SkString basePath = GetResourcePath("fonts/");
  211. SkString fontsXml = GetResourcePath(fontsXmlFilename);
  212. if (!sk_exists(fontsXml.c_str())) {
  213. ERRORF(reporter, "file missing: %s\n", fontsXmlFilename);
  214. return;
  215. }
  216. SkFontMgr_Android_CustomFonts custom;
  217. custom.fSystemFontUse = SkFontMgr_Android_CustomFonts::kOnlyCustom;
  218. custom.fBasePath = basePath.c_str();
  219. custom.fFontsXml = fontsXml.c_str();
  220. custom.fFallbackFontsXml = nullptr;
  221. custom.fIsolated = false;
  222. sk_sp<SkFontMgr> fm(SkFontMgr_New_Android(&custom));
  223. sk_sp<SkTypeface> t(fm->legacyMakeTypeface("non-existent-font", SkFontStyle()));
  224. REPORTER_ASSERT(reporter, nullptr == t);
  225. }
  226. static bool bitmap_compare(const SkBitmap& ref, const SkBitmap& test) {
  227. for (int y = 0; y < test.height(); ++y) {
  228. for (int x = 0; x < test.width(); ++x) {
  229. SkColor testColor = test.getColor(x, y);
  230. SkColor refColor = ref.getColor(x, y);
  231. if (refColor != testColor) {
  232. return false;
  233. }
  234. }
  235. }
  236. return true;
  237. }
  238. DEF_TEST(FontMgrAndroidSystemVariableTypeface, reporter) {
  239. constexpr char fontsXmlFilename[] = "fonts/fonts.xml";
  240. SkString basePath = GetResourcePath("fonts/");
  241. SkString fontsXml = GetResourcePath(fontsXmlFilename);
  242. if (!sk_exists(fontsXml.c_str())) {
  243. ERRORF(reporter, "file missing: %s\n", fontsXmlFilename);
  244. return;
  245. }
  246. SkFontMgr_Android_CustomFonts custom;
  247. custom.fSystemFontUse = SkFontMgr_Android_CustomFonts::kOnlyCustom;
  248. custom.fBasePath = basePath.c_str();
  249. custom.fFontsXml = fontsXml.c_str();
  250. custom.fFallbackFontsXml = nullptr;
  251. custom.fIsolated = false;
  252. sk_sp<SkFontMgr> fontMgr(SkFontMgr_New_Android(&custom));
  253. // "sans-serif" in "fonts/fonts.xml" is "fonts/Distortable.ttf"
  254. sk_sp<SkTypeface> typeface(fontMgr->legacyMakeTypeface("sans-serif", SkFontStyle()));
  255. SkBitmap bitmapStream;
  256. bitmapStream.allocN32Pixels(64, 64);
  257. SkCanvas canvasStream(bitmapStream);
  258. canvasStream.drawColor(SK_ColorWHITE);
  259. SkBitmap bitmapClone;
  260. bitmapClone.allocN32Pixels(64, 64);
  261. SkCanvas canvasClone(bitmapClone);
  262. canvasStream.drawColor(SK_ColorWHITE);
  263. SkPaint paint;
  264. paint.setColor(SK_ColorGRAY);
  265. paint.setAntiAlias(true);
  266. constexpr float kTextSize = 20;
  267. std::unique_ptr<SkStreamAsset> distortableStream(
  268. GetResourceAsStream("fonts/Distortable.ttf"));
  269. if (!distortableStream) {
  270. return;
  271. }
  272. SkPoint point = SkPoint::Make(20.0f, 20.0f);
  273. SkFourByteTag tag = SkSetFourByteTag('w', 'g', 'h', 't');
  274. for (int i = 0; i < 10; ++i) {
  275. SkScalar styleValue =
  276. SkDoubleToScalar(0.5 + i * ((2.0 - 0.5) / 10));
  277. SkFontArguments::VariationPosition::Coordinate
  278. coordinates[] = {{tag, styleValue}};
  279. SkFontArguments::VariationPosition
  280. position = {coordinates, SK_ARRAY_COUNT(coordinates)};
  281. SkFont fontStream(
  282. fontMgr->makeFromStream(distortableStream->duplicate(),
  283. SkFontArguments().setVariationDesignPosition(position)),
  284. kTextSize);
  285. fontStream.setEdging(SkFont::Edging::kSubpixelAntiAlias);
  286. SkFont fontClone(
  287. typeface->makeClone(SkFontArguments().setVariationDesignPosition(position)), kTextSize);
  288. fontClone.setEdging(SkFont::Edging::kSubpixelAntiAlias);
  289. constexpr char text[] = "abc";
  290. canvasStream.drawColor(SK_ColorWHITE);
  291. canvasStream.drawString(text, point.fX, point.fY, fontStream, paint);
  292. canvasClone.drawColor(SK_ColorWHITE);
  293. canvasClone.drawString(text, point.fX, point.fY, fontClone, paint);
  294. bool success = bitmap_compare(bitmapStream, bitmapClone);
  295. REPORTER_ASSERT(reporter, success);
  296. }
  297. }
  298. DEF_TEST(FontMgrAndroidSystemFallbackFor, reporter) {
  299. constexpr char fontsXmlFilename[] = "fonts/fonts.xml";
  300. SkString basePath = GetResourcePath("fonts/");
  301. SkString fontsXml = GetResourcePath(fontsXmlFilename);
  302. if (!sk_exists(fontsXml.c_str())) {
  303. ERRORF(reporter, "file missing: %s\n", fontsXmlFilename);
  304. return;
  305. }
  306. SkFontMgr_Android_CustomFonts custom;
  307. custom.fSystemFontUse = SkFontMgr_Android_CustomFonts::kOnlyCustom;
  308. custom.fBasePath = basePath.c_str();
  309. custom.fFontsXml = fontsXml.c_str();
  310. custom.fFallbackFontsXml = nullptr;
  311. custom.fIsolated = false;
  312. sk_sp<SkFontMgr> fontMgr(SkFontMgr_New_Android(&custom));
  313. // "sans-serif" in "fonts/fonts.xml" is "fonts/Distortable.ttf", which doesn't have a '!'
  314. // but "TestTTC" has a bold font which does have '!' and is marked as fallback for "sans-serif"
  315. // and should take precedence over the same font marked as normal weight next to it.
  316. sk_sp<SkTypeface> typeface(fontMgr->matchFamilyStyleCharacter(
  317. "sans-serif", SkFontStyle(), nullptr, 0, '!'));
  318. REPORTER_ASSERT(reporter, typeface->fontStyle() == SkFontStyle::Bold());
  319. }