1 /* 2 * Copyright 2012 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 8 #include "Test.h" 9 #include "SkPaint.h" 10 #include "SkTypeface.h" 11 #include "SkEndian.h" 12 13 //#define DUMP_TABLES 14 15 #define kFontTableTag_head SkSetFourByteTag('h', 'e', 'a', 'd') 16 #define kFontTableTag_hhea SkSetFourByteTag('h', 'h', 'e', 'a') 17 #define kFontTableTag_maxp SkSetFourByteTag('m', 'a', 'x', 'p') 18 19 static const struct TagSize { 20 SkFontTableTag fTag; 21 size_t fSize; 22 } gKnownTableSizes[] = { 23 { kFontTableTag_head, 54 }, 24 { kFontTableTag_hhea, 36 }, 25 { kFontTableTag_maxp, 32 }, 26 }; 27 28 static void test_unitsPerEm(skiatest::Reporter* reporter, SkTypeface* face) { 29 int upem = face->getUnitsPerEm(); 30 if (0 == upem) return; 31 32 size_t size = face->getTableSize(kFontTableTag_head); 33 if (size) { 34 SkAutoMalloc storage(size); 35 char* ptr = (char*)storage.get(); 36 face->getTableData(kFontTableTag_head, 0, size, ptr); 37 // unitsPerEm is at offset 18 into the 'head' table. 38 int upem2 = SkEndian_SwapBE16(*(uint16_t*)&ptr[18]); 39 REPORTER_ASSERT(reporter, upem2 == upem); 40 } 41 } 42 43 static void test_tables(skiatest::Reporter* reporter, SkTypeface* face) { 44 if (false) { // avoid bit rot, suppress warning 45 SkFontID fontID = face->uniqueID(); 46 REPORTER_ASSERT(reporter, fontID); 47 } 48 49 int count = face->countTables(); 50 51 SkAutoTMalloc<SkFontTableTag> storage(count); 52 SkFontTableTag* tags = storage.get(); 53 54 int count2 = face->getTableTags(tags); 55 REPORTER_ASSERT(reporter, count2 == count); 56 57 for (int i = 0; i < count; ++i) { 58 size_t size = face->getTableSize(tags[i]); 59 REPORTER_ASSERT(reporter, size > 0); 60 61 #ifdef DUMP_TABLES 62 char name[5]; 63 name[0] = (tags[i] >> 24) & 0xFF; 64 name[1] = (tags[i] >> 16) & 0xFF; 65 name[2] = (tags[i] >> 8) & 0xFF; 66 name[3] = (tags[i] >> 0) & 0xFF; 67 name[4] = 0; 68 SkDebugf("%s %d\n", name, size); 69 #endif 70 71 for (size_t j = 0; j < SK_ARRAY_COUNT(gKnownTableSizes); ++j) { 72 if (gKnownTableSizes[j].fTag == tags[i]) { 73 REPORTER_ASSERT(reporter, gKnownTableSizes[j].fSize == size); 74 } 75 } 76 77 // do we get the same size from GetTableData and GetTableSize 78 { 79 SkAutoMalloc data(size); 80 size_t size2 = face->getTableData(tags[i], 0, size, data.get()); 81 REPORTER_ASSERT(reporter, size2 == size); 82 } 83 } 84 } 85 86 static void test_tables(skiatest::Reporter* reporter) { 87 static const char* const gNames[] = { 88 NULL, // default font 89 "Arial", "Times", "Times New Roman", "Helvetica", "Courier", 90 "Courier New", "Terminal", "MS Sans Serif", 91 }; 92 93 for (size_t i = 0; i < SK_ARRAY_COUNT(gNames); ++i) { 94 SkTypeface* face = SkTypeface::CreateFromName(gNames[i], 95 SkTypeface::kNormal); 96 if (face) { 97 #ifdef DUMP_TABLES 98 SkDebugf("%s\n", gNames[i]); 99 #endif 100 test_tables(reporter, face); 101 test_unitsPerEm(reporter, face); 102 face->unref(); 103 } 104 } 105 } 106 107 /* 108 * Verifies that the advance values returned by generateAdvance and 109 * generateMetrics match. 110 */ 111 static void test_advances(skiatest::Reporter* reporter) { 112 static const char* const faces[] = { 113 NULL, // default font 114 "Arial", "Times", "Times New Roman", "Helvetica", "Courier", 115 "Courier New", "Verdana", "monospace", 116 }; 117 118 static const struct { 119 SkPaint::Hinting hinting; 120 unsigned flags; 121 } settings[] = { 122 { SkPaint::kNo_Hinting, 0 }, 123 { SkPaint::kNo_Hinting, SkPaint::kLinearText_Flag }, 124 { SkPaint::kNo_Hinting, SkPaint::kSubpixelText_Flag }, 125 { SkPaint::kSlight_Hinting, 0 }, 126 { SkPaint::kSlight_Hinting, SkPaint::kLinearText_Flag }, 127 { SkPaint::kSlight_Hinting, SkPaint::kSubpixelText_Flag }, 128 { SkPaint::kNormal_Hinting, 0 }, 129 { SkPaint::kNormal_Hinting, SkPaint::kLinearText_Flag }, 130 { SkPaint::kNormal_Hinting, SkPaint::kSubpixelText_Flag }, 131 }; 132 133 static const struct { 134 SkScalar fScaleX; 135 SkScalar fSkewX; 136 } gScaleRec[] = { 137 { SK_Scalar1, 0 }, 138 { SK_Scalar1/2, 0 }, 139 // these two exercise obliquing (skew) 140 { SK_Scalar1, -SK_Scalar1/4 }, 141 { SK_Scalar1/2, -SK_Scalar1/4 }, 142 }; 143 144 SkPaint paint; 145 char txt[] = "long.text.with.lots.of.dots."; 146 147 for (size_t i = 0; i < SK_ARRAY_COUNT(faces); i++) { 148 SkTypeface* face = SkTypeface::CreateFromName(faces[i], SkTypeface::kNormal); 149 paint.setTypeface(face); 150 151 for (size_t j = 0; j < SK_ARRAY_COUNT(settings); j++) { 152 paint.setHinting(settings[j].hinting); 153 paint.setLinearText((settings[j].flags & SkPaint::kLinearText_Flag) != 0); 154 paint.setSubpixelText((settings[j].flags & SkPaint::kSubpixelText_Flag) != 0); 155 156 for (size_t k = 0; k < SK_ARRAY_COUNT(gScaleRec); ++k) { 157 paint.setTextScaleX(gScaleRec[k].fScaleX); 158 paint.setTextSkewX(gScaleRec[k].fSkewX); 159 160 SkRect bounds; 161 162 // For no hinting and light hinting this should take the 163 // optimized generateAdvance path. 164 SkScalar width1 = paint.measureText(txt, strlen(txt)); 165 166 // Requesting the bounds forces a generateMetrics call. 167 SkScalar width2 = paint.measureText(txt, strlen(txt), &bounds); 168 169 // SkDebugf("Font: %s, generateAdvance: %f, generateMetrics: %f\n", 170 // faces[i], SkScalarToFloat(width1), SkScalarToFloat(width2)); 171 172 REPORTER_ASSERT(reporter, width1 == width2); 173 } 174 } 175 } 176 } 177 178 static void TestFontHost(skiatest::Reporter* reporter) { 179 test_tables(reporter); 180 test_advances(reporter); 181 } 182 183 // need tests for SkStrSearch 184 185 #include "TestClassDef.h" 186 DEFINE_TESTCLASS("FontHost", FontHostTestClass, TestFontHost) 187