- add sources.
[platform/framework/web/crosswalk.git] / src / ppapi / tests / test_truetype_font.cc
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.
6
7 #include "ppapi/tests/test_truetype_font.h"
8
9 #include <string.h>
10 #include <algorithm>
11 #include <limits>
12
13 #include "ppapi/c/dev/ppb_testing_dev.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"
20
21 REGISTER_TEST_CASE(TrueTypeFont);
22
23 #define MAKE_TABLE_TAG(a, b, c, d) ((a) << 24) + ((b) << 16) + ((c) << 8) + (d)
24
25 namespace {
26
27 const PP_Resource kInvalidResource = 0;
28 const PP_Instance kInvalidInstance = 0;
29
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;
38 };
39
40 struct FontDirectoryEntry {
41   uint32_t tag;
42   uint32_t checksum;
43   uint32_t offset;
44   uint32_t logical_length;
45 };
46
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);
50 }
51
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);
55 }
56
57 }
58
59 TestTrueTypeFont::TestTrueTypeFont(TestingInstance* instance)
60     : TestCase(instance),
61       ppb_truetype_font_interface_(NULL),
62       ppb_core_interface_(NULL),
63       ppb_var_interface_(NULL) {
64 }
65
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");
71
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");
76
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");
81
82   return
83       ppb_truetype_font_interface_ &&
84       ppb_core_interface_ &&
85       ppb_var_interface_;
86 }
87
88 TestTrueTypeFont::~TestTrueTypeFont() {
89 }
90
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);
98 }
99
100 std::string TestTrueTypeFont::TestGetFontFamilies() {
101   {
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());
113   }
114   {
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());
126   }
127
128   PASS();
129 }
130
131 std::string TestTrueTypeFont::TestGetFontsInFamily() {
132   {
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.
145     }
146
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());
158
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();
167
168       ASSERT_EQ(family, desc.family());
169       ASSERT_EQ(font_in_family.style(), desc.style());
170       ASSERT_EQ(font_in_family.weight(), desc.weight());
171     }
172   }
173   {
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());
187   }
188
189   PASS();
190 }
191
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
201   };
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));
206
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));
211
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));
215
216   PASS();
217 }
218
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());
235
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));
243
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)));
252
253   PASS();
254 }
255
256 std::string TestTrueTypeFont::TestGetTableTags() {
257   pp::TrueTypeFontDesc_Dev desc;
258   pp::TrueTypeFont_Dev font(instance_, desc);
259   {
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')
280     };
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));
286   }
287   {
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());
298   }
299
300   PASS();
301 }
302
303 std::string TestTrueTypeFont::TestGetTable() {
304   pp::TrueTypeFontDesc_Dev desc;
305   pp::TrueTypeFont_Dev font(instance_, desc);
306
307   {
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());
317
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());
327
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;
348       }
349     }
350     ASSERT_NE(NULL, cmap_entry);
351
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()));
359
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));
375   }
376   {
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());
385   }
386   {
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());
400   }
401   {
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());
410   }
411   {
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());
420   }
421   {
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());
431   }
432
433   PASS();
434 }