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