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