Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / ppapi / tests / test_truetype_font.cc
blob2295fe032a8c32e661a5671db1738e299df3cbdf
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 //
5 // Tests PPB_TrueTypeFont interface.
7 #include "ppapi/tests/test_truetype_font.h"
9 #include <string.h>
10 #include <algorithm>
11 #include <limits>
13 #include "ppapi/c/private/ppb_testing_private.h"
14 #include "ppapi/cpp/completion_callback.h"
15 #include "ppapi/cpp/dev/truetype_font_dev.h"
16 #include "ppapi/cpp/instance.h"
17 #include "ppapi/cpp/var.h"
18 #include "ppapi/tests/test_utils.h"
19 #include "ppapi/tests/testing_instance.h"
21 REGISTER_TEST_CASE(TrueTypeFont);
23 #define MAKE_TABLE_TAG(a, b, c, d) ((a) << 24) + ((b) << 16) + ((c) << 8) + (d)
25 namespace {
27 const PP_Resource kInvalidResource = 0;
28 const PP_Instance kInvalidInstance = 0;
30 // TrueType font header and table entry structs. See
31 // https://developer.apple.com/fonts/TTRefMan/RM06/Chap6.html
32 struct FontHeader {
33 int32_t font_type;
34 uint16_t num_tables;
35 uint16_t search_range;
36 uint16_t entry_selector;
37 uint16_t range_shift;
40 struct FontDirectoryEntry {
41 uint32_t tag;
42 uint32_t checksum;
43 uint32_t offset;
44 uint32_t logical_length;
47 uint32_t ReadBigEndian32(const void* ptr) {
48 const uint8_t* data = reinterpret_cast<const uint8_t*>(ptr);
49 return (data[3] << 0) | (data[2] << 8) | (data[1] << 16) | (data[0] << 24);
52 uint16_t ReadBigEndian16(const void* ptr) {
53 const uint8_t* data = reinterpret_cast<const uint8_t*>(ptr);
54 return (data[1] << 0) | (data[0] << 8);
59 TestTrueTypeFont::TestTrueTypeFont(TestingInstance* instance)
60 : TestCase(instance),
61 ppb_truetype_font_interface_(NULL),
62 ppb_core_interface_(NULL),
63 ppb_var_interface_(NULL) {
66 bool TestTrueTypeFont::Init() {
67 ppb_truetype_font_interface_ = static_cast<const PPB_TrueTypeFont_Dev*>(
68 pp::Module::Get()->GetBrowserInterface(PPB_TRUETYPEFONT_DEV_INTERFACE));
69 if (!ppb_truetype_font_interface_)
70 instance_->AppendError("PPB_TrueTypeFont_Dev interface not available");
72 ppb_core_interface_ = static_cast<const PPB_Core*>(
73 pp::Module::Get()->GetBrowserInterface(PPB_CORE_INTERFACE));
74 if (!ppb_core_interface_)
75 instance_->AppendError("PPB_Core interface not available");
77 ppb_var_interface_ = static_cast<const PPB_Var*>(
78 pp::Module::Get()->GetBrowserInterface(PPB_VAR_INTERFACE));
79 if (!ppb_var_interface_)
80 instance_->AppendError("PPB_Var interface not available");
82 return
83 ppb_truetype_font_interface_ &&
84 ppb_core_interface_ &&
85 ppb_var_interface_;
88 TestTrueTypeFont::~TestTrueTypeFont() {
91 void TestTrueTypeFont::RunTests(const std::string& filter) {
92 RUN_TEST(GetFontFamilies, filter);
93 RUN_TEST(GetFontsInFamily, filter);
94 RUN_TEST(Create, filter);
95 RUN_TEST(Describe, filter);
96 RUN_TEST(GetTableTags, filter);
97 RUN_TEST(GetTable, filter);
100 std::string TestTrueTypeFont::TestGetFontFamilies() {
102 // A valid instance should be able to enumerate fonts.
103 TestCompletionCallbackWithOutput< std::vector<pp::Var> > cc(
104 instance_->pp_instance(), false);
105 cc.WaitForResult(pp::TrueTypeFont_Dev::GetFontFamilies(instance_,
106 cc.GetCallback()));
107 const std::vector<pp::Var> font_families = cc.output();
108 // We should get some font families on any platform.
109 ASSERT_NE(0, font_families.size());
110 ASSERT_EQ(static_cast<int32_t>(font_families.size()), cc.result());
111 // Make sure at least one family is a non-empty string.
112 ASSERT_NE(0, font_families[0].AsString().size());
115 // Using an invalid instance should fail.
116 TestCompletionCallbackWithOutput< std::vector<pp::Var> > cc(
117 instance_->pp_instance(), false);
118 cc.WaitForResult(
119 ppb_truetype_font_interface_->GetFontFamilies(
120 kInvalidInstance,
121 cc.GetCallback().output(),
122 cc.GetCallback().pp_completion_callback()));
123 ASSERT_TRUE(cc.result() == PP_ERROR_FAILED ||
124 cc.result() == PP_ERROR_BADARGUMENT);
125 ASSERT_EQ(0, cc.output().size());
128 PASS();
131 std::string TestTrueTypeFont::TestGetFontsInFamily() {
133 // Get the list of all font families.
134 TestCompletionCallbackWithOutput< std::vector<pp::Var> > cc(
135 instance_->pp_instance(), false);
136 cc.WaitForResult(pp::TrueTypeFont_Dev::GetFontFamilies(instance_,
137 cc.GetCallback()));
138 // Try to use a common family that is likely to have multiple variations.
139 const std::vector<pp::Var> families = cc.output();
140 pp::Var family("Arial");
141 if (std::find(families.begin(), families.end(), family) == families.end()) {
142 family = pp::Var("Times");
143 if (std::find(families.begin(), families.end(), family) == families.end())
144 family = families[0]; // Just use the first family.
147 // GetFontsInFamily: A valid instance should be able to enumerate fonts
148 // in a given family.
149 TestCompletionCallbackWithOutput< std::vector<pp::TrueTypeFontDesc_Dev> >
150 cc2(instance_->pp_instance(), false);
151 cc2.WaitForResult(pp::TrueTypeFont_Dev::GetFontsInFamily(
152 instance_,
153 family,
154 cc2.GetCallback()));
155 std::vector<pp::TrueTypeFontDesc_Dev> fonts_in_family = cc2.output();
156 ASSERT_NE(0, fonts_in_family.size());
157 ASSERT_EQ(static_cast<int32_t>(fonts_in_family.size()), cc2.result());
159 // We should be able to create any of the returned fonts without fallback.
160 for (size_t i = 0; i < fonts_in_family.size(); ++i) {
161 pp::TrueTypeFontDesc_Dev& font_in_family = fonts_in_family[i];
162 pp::TrueTypeFont_Dev font(instance_, font_in_family);
163 TestCompletionCallbackWithOutput<pp::TrueTypeFontDesc_Dev> cc(
164 instance_->pp_instance(), false);
165 cc.WaitForResult(font.Describe(cc.GetCallback()));
166 const pp::TrueTypeFontDesc_Dev desc = cc.output();
168 ASSERT_EQ(family, desc.family());
169 ASSERT_EQ(font_in_family.style(), desc.style());
170 ASSERT_EQ(font_in_family.weight(), desc.weight());
174 // Using an invalid instance should fail.
175 TestCompletionCallbackWithOutput< std::vector<pp::TrueTypeFontDesc_Dev> >
176 cc(instance_->pp_instance(), false);
177 pp::Var family("Times");
178 cc.WaitForResult(
179 ppb_truetype_font_interface_->GetFontsInFamily(
180 kInvalidInstance,
181 family.pp_var(),
182 cc.GetCallback().output(),
183 cc.GetCallback().pp_completion_callback()));
184 ASSERT_TRUE(cc.result() == PP_ERROR_FAILED ||
185 cc.result() == PP_ERROR_BADARGUMENT);
186 ASSERT_EQ(0, cc.output().size());
189 PASS();
192 std::string TestTrueTypeFont::TestCreate() {
193 PP_Resource font;
194 PP_TrueTypeFontDesc_Dev desc = {
195 PP_MakeUndefined(),
196 PP_TRUETYPEFONTFAMILY_SERIF,
197 PP_TRUETYPEFONTSTYLE_NORMAL,
198 PP_TRUETYPEFONTWEIGHT_NORMAL,
199 PP_TRUETYPEFONTWIDTH_NORMAL,
200 PP_TRUETYPEFONTCHARSET_DEFAULT
202 // Creating a font from an invalid instance returns an invalid resource.
203 font = ppb_truetype_font_interface_->Create(kInvalidInstance, &desc);
204 ASSERT_EQ(kInvalidResource, font);
205 ASSERT_NE(PP_TRUE, ppb_truetype_font_interface_->IsTrueTypeFont(font));
207 // Creating a font from a valid instance returns a font resource.
208 font = ppb_truetype_font_interface_->Create(instance_->pp_instance(), &desc);
209 ASSERT_NE(kInvalidResource, font);
210 ASSERT_EQ(PP_TRUE, ppb_truetype_font_interface_->IsTrueTypeFont(font));
212 ppb_core_interface_->ReleaseResource(font);
213 // Once released, the resource shouldn't be a font.
214 ASSERT_NE(PP_TRUE, ppb_truetype_font_interface_->IsTrueTypeFont(font));
216 PASS();
219 std::string TestTrueTypeFont::TestDescribe() {
220 pp::TrueTypeFontDesc_Dev create_desc;
221 create_desc.set_generic_family(PP_TRUETYPEFONTFAMILY_SERIF);
222 create_desc.set_style(PP_TRUETYPEFONTSTYLE_NORMAL);
223 create_desc.set_weight(PP_TRUETYPEFONTWEIGHT_NORMAL);
224 pp::TrueTypeFont_Dev font(instance_, create_desc);
225 // Describe: See what font-matching did with a generic font. We should always
226 // be able to Create a generic Serif font.
227 TestCompletionCallbackWithOutput<pp::TrueTypeFontDesc_Dev> cc(
228 instance_->pp_instance(), false);
229 cc.WaitForResult(font.Describe(cc.GetCallback()));
230 const pp::TrueTypeFontDesc_Dev desc = cc.output();
231 ASSERT_NE(0, desc.family().AsString().size());
232 ASSERT_EQ(PP_TRUETYPEFONTFAMILY_SERIF, desc.generic_family());
233 ASSERT_EQ(PP_TRUETYPEFONTSTYLE_NORMAL, desc.style());
234 ASSERT_EQ(PP_TRUETYPEFONTWEIGHT_NORMAL, desc.weight());
236 // Describe an invalid resource should fail.
237 PP_TrueTypeFontDesc_Dev fail_desc;
238 memset(&fail_desc, 0, sizeof(fail_desc));
239 fail_desc.family = PP_MakeUndefined();
240 // Create a shallow copy to check that no data is changed.
241 PP_TrueTypeFontDesc_Dev fail_desc_copy;
242 memcpy(&fail_desc_copy, &fail_desc, sizeof(fail_desc));
244 cc.WaitForResult(
245 ppb_truetype_font_interface_->Describe(
246 kInvalidResource,
247 &fail_desc,
248 cc.GetCallback().pp_completion_callback()));
249 ASSERT_EQ(PP_ERROR_BADRESOURCE, cc.result());
250 ASSERT_EQ(PP_VARTYPE_UNDEFINED, fail_desc.family.type);
251 ASSERT_EQ(0, memcmp(&fail_desc, &fail_desc_copy, sizeof(fail_desc)));
253 PASS();
256 std::string TestTrueTypeFont::TestGetTableTags() {
257 pp::TrueTypeFontDesc_Dev desc;
258 pp::TrueTypeFont_Dev font(instance_, desc);
260 TestCompletionCallbackWithOutput< std::vector<uint32_t> > cc(
261 instance_->pp_instance(), false);
262 cc.WaitForResult(font.GetTableTags(cc.GetCallback()));
263 std::vector<uint32_t> tags = cc.output();
264 ASSERT_NE(0, tags.size());
265 ASSERT_EQ(static_cast<int32_t>(tags.size()), cc.result());
266 // Tags will vary depending on the actual font that the host platform
267 // chooses. Check that all required TrueType tags are present.
268 const int required_tag_count = 9;
269 uint32_t required_tags[required_tag_count] = {
270 // Note: these must be sorted for std::includes below.
271 MAKE_TABLE_TAG('c', 'm', 'a', 'p'),
272 MAKE_TABLE_TAG('g', 'l', 'y', 'f'),
273 MAKE_TABLE_TAG('h', 'e', 'a', 'd'),
274 MAKE_TABLE_TAG('h', 'h', 'e', 'a'),
275 MAKE_TABLE_TAG('h', 'm', 't', 'x'),
276 MAKE_TABLE_TAG('l', 'o', 'c', 'a'),
277 MAKE_TABLE_TAG('m', 'a', 'x', 'p'),
278 MAKE_TABLE_TAG('n', 'a', 'm', 'e'),
279 MAKE_TABLE_TAG('p', 'o', 's', 't')
281 std::sort(tags.begin(), tags.end());
282 ASSERT_TRUE(std::includes(tags.begin(),
283 tags.end(),
284 required_tags,
285 required_tags + required_tag_count));
288 // Invalid resource should fail and write no data.
289 TestCompletionCallbackWithOutput< std::vector<uint32_t> > cc(
290 instance_->pp_instance(), false);
291 cc.WaitForResult(
292 ppb_truetype_font_interface_->GetTableTags(
293 kInvalidResource,
294 cc.GetCallback().output(),
295 cc.GetCallback().pp_completion_callback()));
296 ASSERT_EQ(PP_ERROR_BADRESOURCE, cc.result());
297 ASSERT_EQ(0, cc.output().size());
300 PASS();
303 std::string TestTrueTypeFont::TestGetTable() {
304 pp::TrueTypeFontDesc_Dev desc;
305 pp::TrueTypeFont_Dev font(instance_, desc);
308 // Getting a required table from a valid font should succeed.
309 TestCompletionCallbackWithOutput< std::vector<char> > cc1(
310 instance_->pp_instance(), false);
311 cc1.WaitForResult(font.GetTable(MAKE_TABLE_TAG('c', 'm', 'a', 'p'),
312 0, std::numeric_limits<int32_t>::max(),
313 cc1.GetCallback()));
314 const std::vector<char> cmap_data = cc1.output();
315 ASSERT_NE(0, cmap_data.size());
316 ASSERT_EQ(static_cast<int32_t>(cmap_data.size()), cc1.result());
318 // Passing 0 for the table tag should return the entire font.
319 TestCompletionCallbackWithOutput< std::vector<char> > cc2(
320 instance_->pp_instance(), false);
321 cc2.WaitForResult(font.GetTable(0 /* table_tag */,
322 0, std::numeric_limits<int32_t>::max(),
323 cc2.GetCallback()));
324 const std::vector<char> entire_font = cc2.output();
325 ASSERT_NE(0, entire_font.size());
326 ASSERT_EQ(static_cast<int32_t>(entire_font.size()), cc2.result());
328 // Verify that the CMAP table is in entire_font, and that it's identical
329 // to the one we retrieved above. Note that since the font header and table
330 // directory are in file (big-endian) order, we need to byte swap tags and
331 // numbers.
332 const size_t kHeaderSize = sizeof(FontHeader);
333 const size_t kEntrySize = sizeof(FontDirectoryEntry);
334 ASSERT_TRUE(kHeaderSize < entire_font.size());
335 FontHeader header;
336 memcpy(&header, &entire_font[0], kHeaderSize);
337 uint16_t num_tables = ReadBigEndian16(&header.num_tables);
338 std::vector<FontDirectoryEntry> directory(num_tables);
339 size_t directory_size = kEntrySize * num_tables;
340 ASSERT_TRUE(kHeaderSize + directory_size < entire_font.size());
341 memcpy(&directory[0], &entire_font[kHeaderSize], directory_size);
342 const FontDirectoryEntry* cmap_entry = NULL;
343 for (uint16_t i = 0; i < num_tables; i++) {
344 if (ReadBigEndian32(&directory[i].tag) ==
345 MAKE_TABLE_TAG('c', 'm', 'a', 'p')) {
346 cmap_entry = &directory[i];
347 break;
350 ASSERT_NE(NULL, cmap_entry);
352 uint32_t logical_length = ReadBigEndian32(&cmap_entry->logical_length);
353 uint32_t table_offset = ReadBigEndian32(&cmap_entry->offset);
354 ASSERT_EQ(static_cast<size_t>(logical_length), cmap_data.size());
355 ASSERT_TRUE(static_cast<size_t>(table_offset + logical_length) <
356 entire_font.size());
357 const char* cmap_table = &entire_font[0] + table_offset;
358 ASSERT_EQ(0, memcmp(cmap_table, &cmap_data[0], cmap_data.size()));
360 // Use offset and max_data_length to restrict the data. Read a part of
361 // the 'CMAP' table.
362 TestCompletionCallbackWithOutput< std::vector<char> > cc3(
363 instance_->pp_instance(), false);
364 const int32_t kOffset = 4;
365 int32_t partial_cmap_size = static_cast<int32_t>(cmap_data.size() - 64);
366 cc3.WaitForResult(font.GetTable(MAKE_TABLE_TAG('c', 'm', 'a', 'p'),
367 kOffset,
368 partial_cmap_size,
369 cc3.GetCallback()));
370 const std::vector<char> partial_cmap_data = cc3.output();
371 ASSERT_EQ(partial_cmap_data.size(), static_cast<size_t>(cc3.result()));
372 ASSERT_EQ(partial_cmap_data.size(), static_cast<size_t>(partial_cmap_size));
373 ASSERT_EQ(0, memcmp(cmap_table + kOffset, &partial_cmap_data[0],
374 partial_cmap_size));
377 // Getting an invalid table should fail ('zzzz' should be safely invalid).
378 TestCompletionCallbackWithOutput< std::vector<char> > cc(
379 instance_->pp_instance(), false);
380 cc.WaitForResult(font.GetTable(MAKE_TABLE_TAG('z', 'z', 'z', 'z'),
381 0, std::numeric_limits<int32_t>::max(),
382 cc.GetCallback()));
383 ASSERT_EQ(0, cc.output().size());
384 ASSERT_EQ(PP_ERROR_FAILED, cc.result());
387 // GetTable on an invalid resource should fail with a bad resource error
388 // and write no data.
389 TestCompletionCallbackWithOutput< std::vector<char> > cc(
390 instance_->pp_instance(), false);
391 cc.WaitForResult(
392 ppb_truetype_font_interface_->GetTable(
393 kInvalidResource,
394 MAKE_TABLE_TAG('c', 'm', 'a', 'p'),
395 0, std::numeric_limits<int32_t>::max(),
396 cc.GetCallback().output(),
397 cc.GetCallback().pp_completion_callback()));
398 ASSERT_EQ(PP_ERROR_BADRESOURCE, cc.result());
399 ASSERT_EQ(0, cc.output().size());
402 // Negative offset should fail with a bad argument error and write no data.
403 TestCompletionCallbackWithOutput< std::vector<char> > cc(
404 instance_->pp_instance(), false);
405 cc.WaitForResult(font.GetTable(MAKE_TABLE_TAG('c', 'm', 'a', 'p'),
406 -100, 0,
407 cc.GetCallback()));
408 ASSERT_EQ(PP_ERROR_BADARGUMENT, cc.result());
409 ASSERT_EQ(0, cc.output().size());
412 // Offset larger than file size succeeds but returns no data.
413 TestCompletionCallbackWithOutput< std::vector<char> > cc(
414 instance_->pp_instance(), false);
415 cc.WaitForResult(font.GetTable(MAKE_TABLE_TAG('c', 'm', 'a', 'p'),
416 1 << 28, 0,
417 cc.GetCallback()));
418 ASSERT_EQ(PP_OK, cc.result());
419 ASSERT_EQ(0, cc.output().size());
422 // Negative max_data_length should fail with a bad argument error and write
423 // no data.
424 TestCompletionCallbackWithOutput< std::vector<char> > cc(
425 instance_->pp_instance(), false);
426 cc.WaitForResult(font.GetTable(MAKE_TABLE_TAG('c', 'm', 'a', 'p'),
427 0, -100,
428 cc.GetCallback()));
429 ASSERT_EQ(PP_ERROR_BADARGUMENT, cc.result());
430 ASSERT_EQ(0, cc.output().size());
433 PASS();