blob: 9a186dd57d2fc51855ac2f8d53984562c7414d52 [file] [log] [blame]
reed@google.com17aa07d2012-02-23 14:51:10 +00001/*
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"
reed@google.com8b0a3352012-04-19 18:52:39 +00009#include "SkPaint.h"
reed@google.comed268bf2013-03-11 20:13:36 +000010#include "SkFontStream.h"
11#include "SkStream.h"
reed@google.com17aa07d2012-02-23 14:51:10 +000012#include "SkTypeface.h"
reed@google.com4b2af9c2012-07-31 17:24:44 +000013#include "SkEndian.h"
reed@google.com17aa07d2012-02-23 14:51:10 +000014
15//#define DUMP_TABLES
reed@google.comed268bf2013-03-11 20:13:36 +000016//#define DUMP_TTC_TABLES
reed@google.com17aa07d2012-02-23 14:51:10 +000017
18#define kFontTableTag_head SkSetFourByteTag('h', 'e', 'a', 'd')
19#define kFontTableTag_hhea SkSetFourByteTag('h', 'h', 'e', 'a')
reed@google.com17aa07d2012-02-23 14:51:10 +000020#define kFontTableTag_maxp SkSetFourByteTag('m', 'a', 'x', 'p')
21
22static const struct TagSize {
23 SkFontTableTag fTag;
24 size_t fSize;
25} gKnownTableSizes[] = {
26 { kFontTableTag_head, 54 },
27 { kFontTableTag_hhea, 36 },
reed@google.com17aa07d2012-02-23 14:51:10 +000028 { kFontTableTag_maxp, 32 },
29};
30
reed@google.coma262eea2013-03-21 15:20:00 +000031// Test that getUnitsPerEm() agrees with a direct lookup in the 'head' table
32// (if that table is available.
reed@google.com4b2af9c2012-07-31 17:24:44 +000033static void test_unitsPerEm(skiatest::Reporter* reporter, SkTypeface* face) {
reed@google.coma262eea2013-03-21 15:20:00 +000034 int nativeUPEM = face->getUnitsPerEm();;
reed@google.com4b2af9c2012-07-31 17:24:44 +000035
reed@google.coma262eea2013-03-21 15:20:00 +000036 int tableUPEM = -1;
reed@google.com4b2af9c2012-07-31 17:24:44 +000037 size_t size = face->getTableSize(kFontTableTag_head);
38 if (size) {
39 SkAutoMalloc storage(size);
40 char* ptr = (char*)storage.get();
41 face->getTableData(kFontTableTag_head, 0, size, ptr);
42 // unitsPerEm is at offset 18 into the 'head' table.
reed@google.coma262eea2013-03-21 15:20:00 +000043 tableUPEM = SkEndian_SwapBE16(*(uint16_t*)&ptr[18]);
44 }
45// SkDebugf("--- typeface returned %d upem [%X]\n", nativeUPEM, face->uniqueID());
46
47 if (tableUPEM >= 0) {
48 REPORTER_ASSERT(reporter, tableUPEM == nativeUPEM);
49 } else {
50 // not sure this is a bug, but lets report it for now as info.
51 SkDebugf("--- typeface returned 0 upem [%X]\n", face->uniqueID());
reed@google.com4b2af9c2012-07-31 17:24:44 +000052 }
53}
54
reed@google.comed268bf2013-03-11 20:13:36 +000055static void test_fontstream(skiatest::Reporter* reporter,
56 SkStream* stream, int ttcIndex) {
57 int n = SkFontStream::GetTableTags(stream, ttcIndex, NULL);
58 SkAutoTArray<SkFontTableTag> array(n);
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +000059
reed@google.comed268bf2013-03-11 20:13:36 +000060 int n2 = SkFontStream::GetTableTags(stream, ttcIndex, array.get());
61 REPORTER_ASSERT(reporter, n == n2);
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +000062
reed@google.comed268bf2013-03-11 20:13:36 +000063 for (int i = 0; i < n; ++i) {
64#ifdef DUMP_TTC_TABLES
65 SkString str;
66 SkFontTableTag t = array[i];
67 str.appendUnichar((t >> 24) & 0xFF);
68 str.appendUnichar((t >> 16) & 0xFF);
69 str.appendUnichar((t >> 8) & 0xFF);
70 str.appendUnichar((t >> 0) & 0xFF);
71 SkDebugf("[%d:%d] '%s'\n", ttcIndex, i, str.c_str());
72#endif
73 size_t size = SkFontStream::GetTableSize(stream, ttcIndex, array[i]);
74 for (size_t j = 0; j < SK_ARRAY_COUNT(gKnownTableSizes); ++j) {
75 if (gKnownTableSizes[j].fTag == array[i]) {
76 REPORTER_ASSERT(reporter, gKnownTableSizes[j].fSize == size);
77 }
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +000078 }
reed@google.comed268bf2013-03-11 20:13:36 +000079 }
80}
81
82static void test_fontstream(skiatest::Reporter* reporter, SkStream* stream) {
83 int count = SkFontStream::CountTTCEntries(stream);
84#ifdef DUMP_TTC_TABLES
85 SkDebugf("CountTTCEntries %d\n", count);
86#endif
87 for (int i = 0; i < count; ++i) {
88 test_fontstream(reporter, stream, i);
89 }
90}
91
92static void test_fontstream(skiatest::Reporter* reporter) {
93 // TODO: replace when we get a tools/resources/fonts/test.ttc
94 const char* name = "/AmericanTypewriter.ttc";
95 SkFILEStream stream(name);
96 if (stream.isValid()) {
97 test_fontstream(reporter, &stream);
98 }
99}
100
reed@google.com17aa07d2012-02-23 14:51:10 +0000101static void test_tables(skiatest::Reporter* reporter, SkTypeface* face) {
caryclark@google.com42639cd2012-06-06 12:03:39 +0000102 if (false) { // avoid bit rot, suppress warning
robertphillips@google.com5b332112013-01-30 20:33:12 +0000103 SkFontID fontID = face->uniqueID();
caryclark@google.com42639cd2012-06-06 12:03:39 +0000104 REPORTER_ASSERT(reporter, fontID);
105 }
reed@google.com17aa07d2012-02-23 14:51:10 +0000106
reed@google.com8b0a3352012-04-19 18:52:39 +0000107 int count = face->countTables();
reed@google.com17aa07d2012-02-23 14:51:10 +0000108
109 SkAutoTMalloc<SkFontTableTag> storage(count);
110 SkFontTableTag* tags = storage.get();
reed@google.comfbd033d2012-02-23 16:15:58 +0000111
reed@google.com8b0a3352012-04-19 18:52:39 +0000112 int count2 = face->getTableTags(tags);
reed@google.comfbd033d2012-02-23 16:15:58 +0000113 REPORTER_ASSERT(reporter, count2 == count);
reed@google.com17aa07d2012-02-23 14:51:10 +0000114
115 for (int i = 0; i < count; ++i) {
reed@google.com8b0a3352012-04-19 18:52:39 +0000116 size_t size = face->getTableSize(tags[i]);
reed@google.com17aa07d2012-02-23 14:51:10 +0000117 REPORTER_ASSERT(reporter, size > 0);
118
119#ifdef DUMP_TABLES
120 char name[5];
121 name[0] = (tags[i] >> 24) & 0xFF;
122 name[1] = (tags[i] >> 16) & 0xFF;
123 name[2] = (tags[i] >> 8) & 0xFF;
124 name[3] = (tags[i] >> 0) & 0xFF;
125 name[4] = 0;
126 SkDebugf("%s %d\n", name, size);
127#endif
128
129 for (size_t j = 0; j < SK_ARRAY_COUNT(gKnownTableSizes); ++j) {
130 if (gKnownTableSizes[j].fTag == tags[i]) {
131 REPORTER_ASSERT(reporter, gKnownTableSizes[j].fSize == size);
132 }
133 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000134
reed@google.comfbd033d2012-02-23 16:15:58 +0000135 // do we get the same size from GetTableData and GetTableSize
136 {
137 SkAutoMalloc data(size);
reed@google.com8b0a3352012-04-19 18:52:39 +0000138 size_t size2 = face->getTableData(tags[i], 0, size, data.get());
reed@google.comfbd033d2012-02-23 16:15:58 +0000139 REPORTER_ASSERT(reporter, size2 == size);
140 }
reed@google.com17aa07d2012-02-23 14:51:10 +0000141 }
142}
143
144static void test_tables(skiatest::Reporter* reporter) {
145 static const char* const gNames[] = {
146 NULL, // default font
147 "Arial", "Times", "Times New Roman", "Helvetica", "Courier",
bungeman@google.comc0d3f2f2012-07-31 21:39:05 +0000148 "Courier New", "Terminal", "MS Sans Serif",
reed@google.com17aa07d2012-02-23 14:51:10 +0000149 };
150
151 for (size_t i = 0; i < SK_ARRAY_COUNT(gNames); ++i) {
152 SkTypeface* face = SkTypeface::CreateFromName(gNames[i],
153 SkTypeface::kNormal);
154 if (face) {
155#ifdef DUMP_TABLES
156 SkDebugf("%s\n", gNames[i]);
157#endif
158 test_tables(reporter, face);
reed@google.com4b2af9c2012-07-31 17:24:44 +0000159 test_unitsPerEm(reporter, face);
reed@google.com17aa07d2012-02-23 14:51:10 +0000160 face->unref();
161 }
162 }
163}
164
bungeman@google.com34f10262012-03-23 18:11:47 +0000165/*
166 * Verifies that the advance values returned by generateAdvance and
167 * generateMetrics match.
168 */
169static void test_advances(skiatest::Reporter* reporter) {
170 static const char* const faces[] = {
171 NULL, // default font
172 "Arial", "Times", "Times New Roman", "Helvetica", "Courier",
173 "Courier New", "Verdana", "monospace",
174 };
175
176 static const struct {
177 SkPaint::Hinting hinting;
178 unsigned flags;
179 } settings[] = {
180 { SkPaint::kNo_Hinting, 0 },
181 { SkPaint::kNo_Hinting, SkPaint::kLinearText_Flag },
182 { SkPaint::kNo_Hinting, SkPaint::kSubpixelText_Flag },
183 { SkPaint::kSlight_Hinting, 0 },
184 { SkPaint::kSlight_Hinting, SkPaint::kLinearText_Flag },
185 { SkPaint::kSlight_Hinting, SkPaint::kSubpixelText_Flag },
186 { SkPaint::kNormal_Hinting, 0 },
187 { SkPaint::kNormal_Hinting, SkPaint::kLinearText_Flag },
188 { SkPaint::kNormal_Hinting, SkPaint::kSubpixelText_Flag },
189 };
190
reed@google.comd074c372012-07-18 13:45:58 +0000191 static const struct {
192 SkScalar fScaleX;
193 SkScalar fSkewX;
194 } gScaleRec[] = {
195 { SK_Scalar1, 0 },
196 { SK_Scalar1/2, 0 },
197 // these two exercise obliquing (skew)
198 { SK_Scalar1, -SK_Scalar1/4 },
199 { SK_Scalar1/2, -SK_Scalar1/4 },
200 };
201
bungeman@google.com34f10262012-03-23 18:11:47 +0000202 SkPaint paint;
203 char txt[] = "long.text.with.lots.of.dots.";
204
205 for (size_t i = 0; i < SK_ARRAY_COUNT(faces); i++) {
bungeman@google.com94471032013-02-25 15:55:13 +0000206 SkAutoTUnref<SkTypeface> face(SkTypeface::CreateFromName(faces[i], SkTypeface::kNormal));
bungeman@google.com34f10262012-03-23 18:11:47 +0000207 paint.setTypeface(face);
208
209 for (size_t j = 0; j < SK_ARRAY_COUNT(settings); j++) {
reed@google.comd074c372012-07-18 13:45:58 +0000210 paint.setHinting(settings[j].hinting);
211 paint.setLinearText((settings[j].flags & SkPaint::kLinearText_Flag) != 0);
212 paint.setSubpixelText((settings[j].flags & SkPaint::kSubpixelText_Flag) != 0);
bungeman@google.com34f10262012-03-23 18:11:47 +0000213
reed@google.comd074c372012-07-18 13:45:58 +0000214 for (size_t k = 0; k < SK_ARRAY_COUNT(gScaleRec); ++k) {
215 paint.setTextScaleX(gScaleRec[k].fScaleX);
216 paint.setTextSkewX(gScaleRec[k].fSkewX);
bungeman@google.com34f10262012-03-23 18:11:47 +0000217
reed@google.comd074c372012-07-18 13:45:58 +0000218 SkRect bounds;
bungeman@google.com34f10262012-03-23 18:11:47 +0000219
reed@google.comd074c372012-07-18 13:45:58 +0000220 // For no hinting and light hinting this should take the
221 // optimized generateAdvance path.
222 SkScalar width1 = paint.measureText(txt, strlen(txt));
bungeman@google.com34f10262012-03-23 18:11:47 +0000223
reed@google.comd074c372012-07-18 13:45:58 +0000224 // Requesting the bounds forces a generateMetrics call.
225 SkScalar width2 = paint.measureText(txt, strlen(txt), &bounds);
bungeman@google.com34f10262012-03-23 18:11:47 +0000226
reed@google.comd074c372012-07-18 13:45:58 +0000227 // SkDebugf("Font: %s, generateAdvance: %f, generateMetrics: %f\n",
228 // faces[i], SkScalarToFloat(width1), SkScalarToFloat(width2));
229
230 REPORTER_ASSERT(reporter, width1 == width2);
231 }
bungeman@google.com34f10262012-03-23 18:11:47 +0000232 }
233 }
234}
235
reed@google.com17aa07d2012-02-23 14:51:10 +0000236static void TestFontHost(skiatest::Reporter* reporter) {
237 test_tables(reporter);
reed@google.comed268bf2013-03-11 20:13:36 +0000238 test_fontstream(reporter);
bungeman@google.com34f10262012-03-23 18:11:47 +0000239 test_advances(reporter);
reed@google.com17aa07d2012-02-23 14:51:10 +0000240}
241
242// need tests for SkStrSearch
243
244#include "TestClassDef.h"
245DEFINE_TESTCLASS("FontHost", FontHostTestClass, TestFontHost)