1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2012 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "include/core/SkFont.h" 9cb93a386Sopenharmony_ci#include "include/core/SkPaint.h" 10cb93a386Sopenharmony_ci#include "include/core/SkStream.h" 11cb93a386Sopenharmony_ci#include "include/core/SkTypeface.h" 12cb93a386Sopenharmony_ci#include "src/core/SkAutoMalloc.h" 13cb93a386Sopenharmony_ci#include "src/core/SkEndian.h" 14cb93a386Sopenharmony_ci#include "src/core/SkFontStream.h" 15cb93a386Sopenharmony_ci#include "src/core/SkOSFile.h" 16cb93a386Sopenharmony_ci#include "tests/Test.h" 17cb93a386Sopenharmony_ci#include "tools/Resources.h" 18cb93a386Sopenharmony_ci 19cb93a386Sopenharmony_ci//#define DUMP_TABLES 20cb93a386Sopenharmony_ci//#define DUMP_TTC_TABLES 21cb93a386Sopenharmony_ci 22cb93a386Sopenharmony_ci#define kFontTableTag_head SkSetFourByteTag('h', 'e', 'a', 'd') 23cb93a386Sopenharmony_ci#define kFontTableTag_hhea SkSetFourByteTag('h', 'h', 'e', 'a') 24cb93a386Sopenharmony_ci#define kFontTableTag_maxp SkSetFourByteTag('m', 'a', 'x', 'p') 25cb93a386Sopenharmony_ci 26cb93a386Sopenharmony_cistatic const struct TagSize { 27cb93a386Sopenharmony_ci SkFontTableTag fTag; 28cb93a386Sopenharmony_ci size_t fSize; 29cb93a386Sopenharmony_ci} gKnownTableSizes[] = { 30cb93a386Sopenharmony_ci { kFontTableTag_head, 54 }, 31cb93a386Sopenharmony_ci { kFontTableTag_hhea, 36 }, 32cb93a386Sopenharmony_ci}; 33cb93a386Sopenharmony_ci 34cb93a386Sopenharmony_ci// Test that getUnitsPerEm() agrees with a direct lookup in the 'head' table 35cb93a386Sopenharmony_ci// (if that table is available). 36cb93a386Sopenharmony_cistatic void test_unitsPerEm(skiatest::Reporter* reporter, const sk_sp<SkTypeface>& face) { 37cb93a386Sopenharmony_ci int nativeUPEM = face->getUnitsPerEm(); 38cb93a386Sopenharmony_ci 39cb93a386Sopenharmony_ci int tableUPEM = -1; 40cb93a386Sopenharmony_ci size_t size = face->getTableSize(kFontTableTag_head); 41cb93a386Sopenharmony_ci if (size) { 42cb93a386Sopenharmony_ci // unitsPerEm is at offset 18 into the 'head' table. 43cb93a386Sopenharmony_ci uint16_t rawUPEM; 44cb93a386Sopenharmony_ci face->getTableData(kFontTableTag_head, 18, sizeof(rawUPEM), &rawUPEM); 45cb93a386Sopenharmony_ci tableUPEM = SkEndian_SwapBE16(rawUPEM); 46cb93a386Sopenharmony_ci } 47cb93a386Sopenharmony_ci 48cb93a386Sopenharmony_ci if (tableUPEM >= 0) { 49cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, tableUPEM == nativeUPEM); 50cb93a386Sopenharmony_ci } 51cb93a386Sopenharmony_ci} 52cb93a386Sopenharmony_ci 53cb93a386Sopenharmony_ci// Test that countGlyphs() agrees with a direct lookup in the 'maxp' table 54cb93a386Sopenharmony_ci// (if that table is available). 55cb93a386Sopenharmony_cistatic void test_countGlyphs(skiatest::Reporter* reporter, const sk_sp<SkTypeface>& face) { 56cb93a386Sopenharmony_ci int nativeGlyphs = face->countGlyphs(); 57cb93a386Sopenharmony_ci 58cb93a386Sopenharmony_ci int tableGlyphs = -1; 59cb93a386Sopenharmony_ci size_t size = face->getTableSize(kFontTableTag_maxp); 60cb93a386Sopenharmony_ci if (size) { 61cb93a386Sopenharmony_ci // glyphs is at offset 4 into the 'maxp' table. 62cb93a386Sopenharmony_ci uint16_t rawGlyphs; 63cb93a386Sopenharmony_ci face->getTableData(kFontTableTag_maxp, 4, sizeof(rawGlyphs), &rawGlyphs); 64cb93a386Sopenharmony_ci tableGlyphs = SkEndian_SwapBE16(rawGlyphs); 65cb93a386Sopenharmony_ci } 66cb93a386Sopenharmony_ci 67cb93a386Sopenharmony_ci if (tableGlyphs >= 0) { 68cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, tableGlyphs == nativeGlyphs); 69cb93a386Sopenharmony_ci } 70cb93a386Sopenharmony_ci} 71cb93a386Sopenharmony_ci 72cb93a386Sopenharmony_cistatic void test_fontstream(skiatest::Reporter* reporter, SkStream* stream, int ttcIndex) { 73cb93a386Sopenharmony_ci int n = SkFontStream::GetTableTags(stream, ttcIndex, nullptr); 74cb93a386Sopenharmony_ci SkAutoTArray<SkFontTableTag> array(n); 75cb93a386Sopenharmony_ci 76cb93a386Sopenharmony_ci int n2 = SkFontStream::GetTableTags(stream, ttcIndex, array.get()); 77cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, n == n2); 78cb93a386Sopenharmony_ci 79cb93a386Sopenharmony_ci for (int i = 0; i < n; ++i) { 80cb93a386Sopenharmony_ci#ifdef DUMP_TTC_TABLES 81cb93a386Sopenharmony_ci SkString str; 82cb93a386Sopenharmony_ci SkFontTableTag t = array[i]; 83cb93a386Sopenharmony_ci str.appendUnichar((t >> 24) & 0xFF); 84cb93a386Sopenharmony_ci str.appendUnichar((t >> 16) & 0xFF); 85cb93a386Sopenharmony_ci str.appendUnichar((t >> 8) & 0xFF); 86cb93a386Sopenharmony_ci str.appendUnichar((t >> 0) & 0xFF); 87cb93a386Sopenharmony_ci SkDebugf("[%d:%d] '%s'\n", ttcIndex, i, str.c_str()); 88cb93a386Sopenharmony_ci#endif 89cb93a386Sopenharmony_ci size_t size = SkFontStream::GetTableSize(stream, ttcIndex, array[i]); 90cb93a386Sopenharmony_ci for (size_t j = 0; j < SK_ARRAY_COUNT(gKnownTableSizes); ++j) { 91cb93a386Sopenharmony_ci if (gKnownTableSizes[j].fTag == array[i]) { 92cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, gKnownTableSizes[j].fSize == size); 93cb93a386Sopenharmony_ci } 94cb93a386Sopenharmony_ci } 95cb93a386Sopenharmony_ci } 96cb93a386Sopenharmony_ci} 97cb93a386Sopenharmony_ci 98cb93a386Sopenharmony_cistatic void test_fontstream(skiatest::Reporter* reporter) { 99cb93a386Sopenharmony_ci std::unique_ptr<SkStreamAsset> stream(GetResourceAsStream("fonts/test.ttc")); 100cb93a386Sopenharmony_ci if (!stream) { 101cb93a386Sopenharmony_ci SkDebugf("Skipping FontHostTest::test_fontstream\n"); 102cb93a386Sopenharmony_ci return; 103cb93a386Sopenharmony_ci } 104cb93a386Sopenharmony_ci 105cb93a386Sopenharmony_ci int count = SkFontStream::CountTTCEntries(stream.get()); 106cb93a386Sopenharmony_ci#ifdef DUMP_TTC_TABLES 107cb93a386Sopenharmony_ci SkDebugf("CountTTCEntries %d\n", count); 108cb93a386Sopenharmony_ci#endif 109cb93a386Sopenharmony_ci for (int i = 0; i < count; ++i) { 110cb93a386Sopenharmony_ci test_fontstream(reporter, stream.get(), i); 111cb93a386Sopenharmony_ci } 112cb93a386Sopenharmony_ci} 113cb93a386Sopenharmony_ci 114cb93a386Sopenharmony_ci// Exercise this rare cmap format (platform 3, encoding 0) 115cb93a386Sopenharmony_cistatic void test_symbolfont(skiatest::Reporter* reporter) { 116cb93a386Sopenharmony_ci auto tf = MakeResourceAsTypeface("fonts/SpiderSymbol.ttf"); 117cb93a386Sopenharmony_ci if (tf) { 118cb93a386Sopenharmony_ci SkUnichar c = 0xf021; 119cb93a386Sopenharmony_ci uint16_t g = SkFont(tf).unicharToGlyph(c); 120cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, g == 3); 121cb93a386Sopenharmony_ci } else { 122cb93a386Sopenharmony_ci // not all platforms support data fonts, so we just note that failure 123cb93a386Sopenharmony_ci SkDebugf("Skipping FontHostTest::test_symbolfont\n"); 124cb93a386Sopenharmony_ci } 125cb93a386Sopenharmony_ci} 126cb93a386Sopenharmony_ci 127cb93a386Sopenharmony_cistatic void test_tables(skiatest::Reporter* reporter, const sk_sp<SkTypeface>& face) { 128cb93a386Sopenharmony_ci if (false) { // avoid bit rot, suppress warning 129cb93a386Sopenharmony_ci SkFontID fontID = face->uniqueID(); 130cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, fontID); 131cb93a386Sopenharmony_ci } 132cb93a386Sopenharmony_ci 133cb93a386Sopenharmony_ci int count = face->countTables(); 134cb93a386Sopenharmony_ci 135cb93a386Sopenharmony_ci SkAutoTMalloc<SkFontTableTag> storage(count); 136cb93a386Sopenharmony_ci SkFontTableTag* tags = storage.get(); 137cb93a386Sopenharmony_ci 138cb93a386Sopenharmony_ci int count2 = face->getTableTags(tags); 139cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, count2 == count); 140cb93a386Sopenharmony_ci 141cb93a386Sopenharmony_ci for (int i = 0; i < count; ++i) { 142cb93a386Sopenharmony_ci size_t size = face->getTableSize(tags[i]); 143cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, size > 0); 144cb93a386Sopenharmony_ci 145cb93a386Sopenharmony_ci#ifdef DUMP_TABLES 146cb93a386Sopenharmony_ci char name[5]; 147cb93a386Sopenharmony_ci name[0] = (tags[i] >> 24) & 0xFF; 148cb93a386Sopenharmony_ci name[1] = (tags[i] >> 16) & 0xFF; 149cb93a386Sopenharmony_ci name[2] = (tags[i] >> 8) & 0xFF; 150cb93a386Sopenharmony_ci name[3] = (tags[i] >> 0) & 0xFF; 151cb93a386Sopenharmony_ci name[4] = 0; 152cb93a386Sopenharmony_ci SkDebugf("%s %d\n", name, size); 153cb93a386Sopenharmony_ci#endif 154cb93a386Sopenharmony_ci 155cb93a386Sopenharmony_ci for (size_t j = 0; j < SK_ARRAY_COUNT(gKnownTableSizes); ++j) { 156cb93a386Sopenharmony_ci if (gKnownTableSizes[j].fTag == tags[i]) { 157cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, gKnownTableSizes[j].fSize == size); 158cb93a386Sopenharmony_ci } 159cb93a386Sopenharmony_ci } 160cb93a386Sopenharmony_ci 161cb93a386Sopenharmony_ci // do we get the same size from GetTableData and GetTableSize 162cb93a386Sopenharmony_ci { 163cb93a386Sopenharmony_ci SkAutoMalloc data(size); 164cb93a386Sopenharmony_ci size_t size2 = face->getTableData(tags[i], 0, size, data.get()); 165cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, size2 == size); 166cb93a386Sopenharmony_ci sk_sp<SkData> data2 = face->copyTableData(tags[i]); 167cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, size == data2->size()); 168cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, !memcmp(data.get(), data2->data(), size)); 169cb93a386Sopenharmony_ci } 170cb93a386Sopenharmony_ci } 171cb93a386Sopenharmony_ci} 172cb93a386Sopenharmony_ci 173cb93a386Sopenharmony_cistatic void test_tables(skiatest::Reporter* reporter) { 174cb93a386Sopenharmony_ci static const char* const gNames[] = { 175cb93a386Sopenharmony_ci nullptr, // default font 176cb93a386Sopenharmony_ci "Helvetica", "Arial", 177cb93a386Sopenharmony_ci "Times", "Times New Roman", 178cb93a386Sopenharmony_ci "Courier", "Courier New", 179cb93a386Sopenharmony_ci "Terminal", "MS Sans Serif", 180cb93a386Sopenharmony_ci "Hiragino Mincho ProN", "MS PGothic", 181cb93a386Sopenharmony_ci }; 182cb93a386Sopenharmony_ci 183cb93a386Sopenharmony_ci for (size_t i = 0; i < SK_ARRAY_COUNT(gNames); ++i) { 184cb93a386Sopenharmony_ci sk_sp<SkTypeface> face(SkTypeface::MakeFromName(gNames[i], SkFontStyle())); 185cb93a386Sopenharmony_ci if (face) { 186cb93a386Sopenharmony_ci#ifdef DUMP_TABLES 187cb93a386Sopenharmony_ci SkDebugf("%s\n", gNames[i]); 188cb93a386Sopenharmony_ci#endif 189cb93a386Sopenharmony_ci test_tables(reporter, face); 190cb93a386Sopenharmony_ci test_unitsPerEm(reporter, face); 191cb93a386Sopenharmony_ci test_countGlyphs(reporter, face); 192cb93a386Sopenharmony_ci } 193cb93a386Sopenharmony_ci } 194cb93a386Sopenharmony_ci} 195cb93a386Sopenharmony_ci 196cb93a386Sopenharmony_ci/* 197cb93a386Sopenharmony_ci * Verifies that the advance values returned by generateAdvance and 198cb93a386Sopenharmony_ci * generateMetrics match. 199cb93a386Sopenharmony_ci */ 200cb93a386Sopenharmony_cistatic void test_advances(skiatest::Reporter* reporter) { 201cb93a386Sopenharmony_ci static const char* const faces[] = { 202cb93a386Sopenharmony_ci nullptr, // default font 203cb93a386Sopenharmony_ci "Arial", "Times", "Times New Roman", "Helvetica", "Courier", 204cb93a386Sopenharmony_ci "Courier New", "Verdana", "monospace", 205cb93a386Sopenharmony_ci }; 206cb93a386Sopenharmony_ci 207cb93a386Sopenharmony_ci static const struct { 208cb93a386Sopenharmony_ci SkFontHinting hinting; 209cb93a386Sopenharmony_ci bool linear; 210cb93a386Sopenharmony_ci bool subpixel; 211cb93a386Sopenharmony_ci } settings[] = { 212cb93a386Sopenharmony_ci { SkFontHinting::kNone, false, false }, 213cb93a386Sopenharmony_ci { SkFontHinting::kNone, true, false }, 214cb93a386Sopenharmony_ci { SkFontHinting::kNone, false, true }, 215cb93a386Sopenharmony_ci { SkFontHinting::kSlight, false, false }, 216cb93a386Sopenharmony_ci { SkFontHinting::kSlight, true, false }, 217cb93a386Sopenharmony_ci { SkFontHinting::kSlight, false, true }, 218cb93a386Sopenharmony_ci { SkFontHinting::kNormal, false, false }, 219cb93a386Sopenharmony_ci { SkFontHinting::kNormal, true, false }, 220cb93a386Sopenharmony_ci { SkFontHinting::kNormal, false, true }, 221cb93a386Sopenharmony_ci }; 222cb93a386Sopenharmony_ci 223cb93a386Sopenharmony_ci static const struct { 224cb93a386Sopenharmony_ci SkScalar fScaleX; 225cb93a386Sopenharmony_ci SkScalar fSkewX; 226cb93a386Sopenharmony_ci } gScaleRec[] = { 227cb93a386Sopenharmony_ci { SK_Scalar1, 0 }, 228cb93a386Sopenharmony_ci { SK_Scalar1/2, 0 }, 229cb93a386Sopenharmony_ci // these two exercise obliquing (skew) 230cb93a386Sopenharmony_ci { SK_Scalar1, -SK_Scalar1/4 }, 231cb93a386Sopenharmony_ci { SK_Scalar1/2, -SK_Scalar1/4 }, 232cb93a386Sopenharmony_ci }; 233cb93a386Sopenharmony_ci 234cb93a386Sopenharmony_ci SkFont font; 235cb93a386Sopenharmony_ci char const * const txt = "long.text.with.lots.of.dots."; 236cb93a386Sopenharmony_ci size_t textLen = strlen(txt); 237cb93a386Sopenharmony_ci 238cb93a386Sopenharmony_ci for (size_t i = 0; i < SK_ARRAY_COUNT(faces); i++) { 239cb93a386Sopenharmony_ci font.setTypeface(SkTypeface::MakeFromName(faces[i], SkFontStyle())); 240cb93a386Sopenharmony_ci 241cb93a386Sopenharmony_ci for (size_t j = 0; j < SK_ARRAY_COUNT(settings); j++) { 242cb93a386Sopenharmony_ci font.setHinting(settings[j].hinting); 243cb93a386Sopenharmony_ci font.setLinearMetrics(settings[j].linear); 244cb93a386Sopenharmony_ci font.setSubpixel(settings[j].subpixel); 245cb93a386Sopenharmony_ci 246cb93a386Sopenharmony_ci for (size_t k = 0; k < SK_ARRAY_COUNT(gScaleRec); ++k) { 247cb93a386Sopenharmony_ci font.setScaleX(gScaleRec[k].fScaleX); 248cb93a386Sopenharmony_ci font.setSkewX(gScaleRec[k].fSkewX); 249cb93a386Sopenharmony_ci 250cb93a386Sopenharmony_ci SkRect bounds; 251cb93a386Sopenharmony_ci 252cb93a386Sopenharmony_ci // For no hinting and light hinting this should take the 253cb93a386Sopenharmony_ci // optimized generateAdvance path. 254cb93a386Sopenharmony_ci SkScalar width1 = font.measureText(txt, textLen, SkTextEncoding::kUTF8); 255cb93a386Sopenharmony_ci 256cb93a386Sopenharmony_ci // Requesting the bounds forces a generateMetrics call. 257cb93a386Sopenharmony_ci SkScalar width2 = font.measureText(txt, textLen, SkTextEncoding::kUTF8, &bounds); 258cb93a386Sopenharmony_ci 259cb93a386Sopenharmony_ci // SkDebugf("Font: %s, generateAdvance: %f, generateMetrics: %f\n", 260cb93a386Sopenharmony_ci // faces[i], SkScalarToFloat(width1), SkScalarToFloat(width2)); 261cb93a386Sopenharmony_ci 262cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, width1 == width2); 263cb93a386Sopenharmony_ci } 264cb93a386Sopenharmony_ci } 265cb93a386Sopenharmony_ci } 266cb93a386Sopenharmony_ci} 267cb93a386Sopenharmony_ci 268cb93a386Sopenharmony_ciDEF_TEST(FontHost, reporter) { 269cb93a386Sopenharmony_ci test_tables(reporter); 270cb93a386Sopenharmony_ci test_fontstream(reporter); 271cb93a386Sopenharmony_ci test_advances(reporter); 272cb93a386Sopenharmony_ci test_symbolfont(reporter); 273cb93a386Sopenharmony_ci} 274cb93a386Sopenharmony_ci 275cb93a386Sopenharmony_ci// need tests for SkStrSearch 276