Home | History | Annotate | Download | only in tests
      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 }
    435