xref: /aosp_15_r20/external/skia/tests/FontHostTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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/SkDebug.h"
19 #include "include/private/base/SkTemplates.h"
20 #include "src/base/SkAutoMalloc.h"
21 #include "src/base/SkEndian.h"
22 #include "src/core/SkFontStream.h"
23 #include "tests/Test.h"
24 #include "tools/Resources.h"
25 #include "tools/fonts/FontToolUtils.h"
26 
27 #include <cstdint>
28 #include <cstring>
29 #include <memory>
30 #include <string>
31 
32 using namespace skia_private;
33 
34 //#define DUMP_TABLES
35 //#define DUMP_TTC_TABLES
36 
37 #define kFontTableTag_head          SkSetFourByteTag('h', 'e', 'a', 'd')
38 #define kFontTableTag_hhea          SkSetFourByteTag('h', 'h', 'e', 'a')
39 #define kFontTableTag_maxp          SkSetFourByteTag('m', 'a', 'x', 'p')
40 
41 static const struct TagSize {
42     SkFontTableTag  fTag;
43     size_t          fSize;
44 } gKnownTableSizes[] = {
45     {   kFontTableTag_head,         54 },
46     {   kFontTableTag_hhea,         36 },
47 };
48 
49 // Test that getUnitsPerEm() agrees with a direct lookup in the 'head' table
50 // (if that table is available).
test_unitsPerEm(skiatest::Reporter * reporter,const sk_sp<SkTypeface> & face)51 static void test_unitsPerEm(skiatest::Reporter* reporter, const sk_sp<SkTypeface>& face) {
52     int nativeUPEM = face->getUnitsPerEm();
53 
54     int tableUPEM = -1;
55     size_t size = face->getTableSize(kFontTableTag_head);
56     if (size) {
57         // unitsPerEm is at offset 18 into the 'head' table.
58         uint16_t rawUPEM;
59         face->getTableData(kFontTableTag_head, 18, sizeof(rawUPEM), &rawUPEM);
60         tableUPEM = SkEndian_SwapBE16(rawUPEM);
61     }
62 
63     if (tableUPEM >= 0) {
64         REPORTER_ASSERT(reporter, tableUPEM == nativeUPEM);
65     }
66 }
67 
68 // Test that countGlyphs() agrees with a direct lookup in the 'maxp' table
69 // (if that table is available).
test_countGlyphs(skiatest::Reporter * reporter,const sk_sp<SkTypeface> & face)70 static void test_countGlyphs(skiatest::Reporter* reporter, const sk_sp<SkTypeface>& face) {
71     int nativeGlyphs = face->countGlyphs();
72 
73     int tableGlyphs = -1;
74     size_t size = face->getTableSize(kFontTableTag_maxp);
75     if (size) {
76         // glyphs is at offset 4 into the 'maxp' table.
77         uint16_t rawGlyphs;
78         face->getTableData(kFontTableTag_maxp, 4, sizeof(rawGlyphs), &rawGlyphs);
79         tableGlyphs = SkEndian_SwapBE16(rawGlyphs);
80     }
81 
82     if (tableGlyphs >= 0) {
83         REPORTER_ASSERT(reporter, tableGlyphs == nativeGlyphs);
84     }
85 }
86 
test_fontstream(skiatest::Reporter * reporter,SkStream * stream,int ttcIndex)87 static void test_fontstream(skiatest::Reporter* reporter, SkStream* stream, int ttcIndex) {
88     int n = SkFontStream::GetTableTags(stream, ttcIndex, nullptr);
89     AutoTArray<SkFontTableTag> array(n);
90 
91     int n2 = SkFontStream::GetTableTags(stream, ttcIndex, array.get());
92     REPORTER_ASSERT(reporter, n == n2);
93 
94     for (int i = 0; i < n; ++i) {
95 #ifdef DUMP_TTC_TABLES
96         SkString str;
97         SkFontTableTag t = array[i];
98         str.appendUnichar((t >> 24) & 0xFF);
99         str.appendUnichar((t >> 16) & 0xFF);
100         str.appendUnichar((t >>  8) & 0xFF);
101         str.appendUnichar((t >>  0) & 0xFF);
102         SkDebugf("[%d:%d] '%s'\n", ttcIndex, i, str.c_str());
103 #endif
104         size_t size = SkFontStream::GetTableSize(stream, ttcIndex, array[i]);
105         for (size_t j = 0; j < std::size(gKnownTableSizes); ++j) {
106             if (gKnownTableSizes[j].fTag == array[i]) {
107                 REPORTER_ASSERT(reporter, gKnownTableSizes[j].fSize == size);
108             }
109         }
110     }
111 }
112 
test_fontstream(skiatest::Reporter * reporter)113 static void test_fontstream(skiatest::Reporter* reporter) {
114     std::unique_ptr<SkStreamAsset> stream(GetResourceAsStream("fonts/test.ttc"));
115     if (!stream) {
116         SkDebugf("Skipping FontHostTest::test_fontstream\n");
117         return;
118     }
119 
120     int count = SkFontStream::CountTTCEntries(stream.get());
121 #ifdef DUMP_TTC_TABLES
122     SkDebugf("CountTTCEntries %d\n", count);
123 #endif
124     for (int i = 0; i < count; ++i) {
125         test_fontstream(reporter, stream.get(), i);
126     }
127 }
128 
129 // Exercise this rare cmap format (platform 3, encoding 0)
test_symbolfont(skiatest::Reporter * reporter)130 static void test_symbolfont(skiatest::Reporter* reporter) {
131     auto tf = ToolUtils::CreateTypefaceFromResource("fonts/SpiderSymbol.ttf");
132     if (tf) {
133         SkUnichar c = 0xf021;
134         uint16_t g = SkFont(tf).unicharToGlyph(c);
135         REPORTER_ASSERT(reporter, g == 3);
136     } else {
137         // not all platforms support data fonts, so we just note that failure
138         SkDebugf("Skipping FontHostTest::test_symbolfont\n");
139     }
140 }
141 
test_tables(skiatest::Reporter * reporter,const sk_sp<SkTypeface> & face)142 static void test_tables(skiatest::Reporter* reporter, const sk_sp<SkTypeface>& face) {
143     if ((false)) { // avoid bit rot, suppress warning
144         SkTypefaceID typefaceID = face->uniqueID();
145         REPORTER_ASSERT(reporter, typefaceID);
146     }
147 
148     int count = face->countTables();
149 
150     AutoTMalloc<SkFontTableTag> storage(count);
151     SkFontTableTag* tags = storage.get();
152 
153     int count2 = face->getTableTags(tags);
154     REPORTER_ASSERT(reporter, count2 == count);
155 
156     for (int i = 0; i < count; ++i) {
157         size_t size = face->getTableSize(tags[i]);
158         REPORTER_ASSERT(reporter, size > 0);
159 
160 #ifdef DUMP_TABLES
161         char name[5];
162         name[0] = (tags[i] >> 24) & 0xFF;
163         name[1] = (tags[i] >> 16) & 0xFF;
164         name[2] = (tags[i] >>  8) & 0xFF;
165         name[3] = (tags[i] >>  0) & 0xFF;
166         name[4] = 0;
167         SkDebugf("%s %d\n", name, size);
168 #endif
169 
170         for (size_t j = 0; j < std::size(gKnownTableSizes); ++j) {
171             if (gKnownTableSizes[j].fTag == tags[i]) {
172                 REPORTER_ASSERT(reporter, gKnownTableSizes[j].fSize == size);
173             }
174         }
175 
176         // do we get the same size from GetTableData and GetTableSize
177         {
178             SkAutoMalloc data(size);
179             size_t size2 = face->getTableData(tags[i], 0, size, data.get());
180             REPORTER_ASSERT(reporter, size2 == size);
181             sk_sp<SkData> data2 = face->copyTableData(tags[i]);
182             REPORTER_ASSERT(reporter, size == data2->size());
183             REPORTER_ASSERT(reporter, !memcmp(data.get(), data2->data(), size));
184         }
185     }
186 }
187 
test_tables(skiatest::Reporter * reporter)188 static void test_tables(skiatest::Reporter* reporter) {
189     static const char* const gNames[] = {
190         nullptr,   // default font
191         "Helvetica", "Arial",
192         "Times", "Times New Roman",
193         "Courier", "Courier New",
194         "Terminal", "MS Sans Serif",
195         "Hiragino Mincho ProN", "MS PGothic",
196     };
197 
198     for (size_t i = 0; i < std::size(gNames); ++i) {
199         sk_sp<SkTypeface> face(ToolUtils::CreateTestTypeface(gNames[i], SkFontStyle()));
200         if (face) {
201 #ifdef DUMP_TABLES
202             SkDebugf("%s\n", gNames[i]);
203 #endif
204             test_tables(reporter, face);
205             test_unitsPerEm(reporter, face);
206             test_countGlyphs(reporter, face);
207         }
208     }
209 }
210 
211 /*
212  * Verifies that the advance values returned by various methods 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(ToolUtils::CreateTestTypeface(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                 SkScalar width1 = font.measureText(txt, textLen, SkTextEncoding::kUTF8);
267 
268                 // Requesting the bounds forces a generateMetrics call.
269                 SkScalar width2 = font.measureText(txt, textLen, SkTextEncoding::kUTF8, &bounds);
270 
271                 REPORTER_ASSERT(reporter, width1 == width2);
272             }
273         }
274     }
275 }
276 
DEF_TEST(FontHost,reporter)277 DEF_TEST(FontHost, reporter) {
278     test_tables(reporter);
279     test_fontstream(reporter);
280     test_advances(reporter);
281     test_symbolfont(reporter);
282 }
283 
284 // need tests for SkStrSearch
285