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.
5 // Tests PPB_TrueTypeFont interface.
7 #include "ppapi/tests/test_truetype_font.h"
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"
21 REGISTER_TEST_CASE(TrueTypeFont
);
23 #define MAKE_TABLE_TAG(a, b, c, d) ((a) << 24) + ((b) << 16) + ((c) << 8) + (d)
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
35 uint16_t search_range
;
36 uint16_t entry_selector
;
40 struct FontDirectoryEntry
{
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
)
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 ppb_core_interface_
= static_cast<const PPB_Core
*>(
70 pp::Module::Get()->GetBrowserInterface(PPB_CORE_INTERFACE
));
71 ppb_var_interface_
= static_cast<const PPB_Var
*>(
72 pp::Module::Get()->GetBrowserInterface(PPB_VAR_INTERFACE
));
73 if (!ppb_truetype_font_interface_
)
74 instance_
->AppendError("PPB_TrueTypeFont_Dev interface not available");
75 if (!ppb_core_interface_
)
76 instance_
->AppendError("PPB_Core interface not available");
77 if (!ppb_var_interface_
)
78 instance_
->AppendError("PPB_Var interface not available");
83 TestTrueTypeFont::~TestTrueTypeFont() {
86 void TestTrueTypeFont::RunTests(const std::string
& filter
) {
87 RUN_TEST(GetFontFamilies
, filter
);
88 RUN_TEST(GetFontsInFamily
, filter
);
89 RUN_TEST(Create
, filter
);
90 RUN_TEST(Describe
, filter
);
91 RUN_TEST(GetTableTags
, filter
);
92 RUN_TEST(GetTable
, filter
);
95 std::string
TestTrueTypeFont::TestGetFontFamilies() {
97 // A valid instance should be able to enumerate fonts.
98 TestCompletionCallbackWithOutput
< std::vector
<pp::Var
> > cc(
99 instance_
->pp_instance(), false);
100 cc
.WaitForResult(pp::TrueTypeFont_Dev::GetFontFamilies(instance_
,
102 const std::vector
<pp::Var
> font_families
= cc
.output();
103 // We should get some font families on any platform.
104 ASSERT_NE(0, font_families
.size());
105 ASSERT_EQ(static_cast<int32_t>(font_families
.size()), cc
.result());
106 // Make sure at least one family is a non-empty string.
107 ASSERT_NE(0, font_families
[0].AsString().size());
110 // Using an invalid instance should fail.
111 TestCompletionCallbackWithOutput
< std::vector
<pp::Var
> > cc(
112 instance_
->pp_instance(), false);
114 ppb_truetype_font_interface_
->GetFontFamilies(
116 cc
.GetCallback().output(),
117 cc
.GetCallback().pp_completion_callback()));
118 ASSERT_TRUE(cc
.result() == PP_ERROR_FAILED
||
119 cc
.result() == PP_ERROR_BADARGUMENT
);
120 ASSERT_EQ(0, cc
.output().size());
126 std::string
TestTrueTypeFont::TestGetFontsInFamily() {
128 // Get the list of all font families.
129 TestCompletionCallbackWithOutput
< std::vector
<pp::Var
> > cc(
130 instance_
->pp_instance(), false);
131 cc
.WaitForResult(pp::TrueTypeFont_Dev::GetFontFamilies(instance_
,
133 // Try to use a common family that is likely to have multiple variations.
134 const std::vector
<pp::Var
> families
= cc
.output();
135 pp::Var
family("Arial");
136 if (std::find(families
.begin(), families
.end(), family
) == families
.end()) {
137 family
= pp::Var("Times");
138 if (std::find(families
.begin(), families
.end(), family
) == families
.end())
139 family
= families
[0]; // Just use the first family.
142 // GetFontsInFamily: A valid instance should be able to enumerate fonts
143 // in a given family.
144 TestCompletionCallbackWithOutput
< std::vector
<pp::TrueTypeFontDesc_Dev
> >
145 cc2(instance_
->pp_instance(), false);
146 cc2
.WaitForResult(pp::TrueTypeFont_Dev::GetFontsInFamily(
150 std::vector
<pp::TrueTypeFontDesc_Dev
> fonts_in_family
= cc2
.output();
151 ASSERT_NE(0, fonts_in_family
.size());
152 ASSERT_EQ(static_cast<int32_t>(fonts_in_family
.size()), cc2
.result());
154 // We should be able to create any of the returned fonts without fallback.
155 for (size_t i
= 0; i
< fonts_in_family
.size(); ++i
) {
156 pp::TrueTypeFontDesc_Dev
& font_in_family
= fonts_in_family
[i
];
157 pp::TrueTypeFont_Dev
font(instance_
, font_in_family
);
158 TestCompletionCallbackWithOutput
<pp::TrueTypeFontDesc_Dev
> cc(
159 instance_
->pp_instance(), false);
160 cc
.WaitForResult(font
.Describe(cc
.GetCallback()));
161 const pp::TrueTypeFontDesc_Dev desc
= cc
.output();
163 ASSERT_EQ(family
, desc
.family());
164 ASSERT_EQ(font_in_family
.style(), desc
.style());
165 ASSERT_EQ(font_in_family
.weight(), desc
.weight());
169 // Using an invalid instance should fail.
170 TestCompletionCallbackWithOutput
< std::vector
<pp::TrueTypeFontDesc_Dev
> >
171 cc(instance_
->pp_instance(), false);
172 pp::Var
family("Times");
174 ppb_truetype_font_interface_
->GetFontsInFamily(
177 cc
.GetCallback().output(),
178 cc
.GetCallback().pp_completion_callback()));
179 ASSERT_TRUE(cc
.result() == PP_ERROR_FAILED
||
180 cc
.result() == PP_ERROR_BADARGUMENT
);
181 ASSERT_EQ(0, cc
.output().size());
187 std::string
TestTrueTypeFont::TestCreate() {
189 PP_TrueTypeFontDesc_Dev desc
= {
191 PP_TRUETYPEFONTFAMILY_SERIF
,
192 PP_TRUETYPEFONTSTYLE_NORMAL
,
193 PP_TRUETYPEFONTWEIGHT_NORMAL
,
194 PP_TRUETYPEFONTWIDTH_NORMAL
,
195 PP_TRUETYPEFONTCHARSET_DEFAULT
197 // Creating a font from an invalid instance returns an invalid resource.
198 font
= ppb_truetype_font_interface_
->Create(kInvalidInstance
, &desc
);
199 ASSERT_EQ(kInvalidResource
, font
);
200 ASSERT_NE(PP_TRUE
, ppb_truetype_font_interface_
->IsTrueTypeFont(font
));
202 // Creating a font from a valid instance returns a font resource.
203 font
= ppb_truetype_font_interface_
->Create(instance_
->pp_instance(), &desc
);
204 ASSERT_NE(kInvalidResource
, font
);
205 ASSERT_EQ(PP_TRUE
, ppb_truetype_font_interface_
->IsTrueTypeFont(font
));
207 ppb_core_interface_
->ReleaseResource(font
);
208 // Once released, the resource shouldn't be a font.
209 ASSERT_NE(PP_TRUE
, ppb_truetype_font_interface_
->IsTrueTypeFont(font
));
214 std::string
TestTrueTypeFont::TestDescribe() {
215 pp::TrueTypeFontDesc_Dev create_desc
;
216 create_desc
.set_generic_family(PP_TRUETYPEFONTFAMILY_SERIF
);
217 create_desc
.set_style(PP_TRUETYPEFONTSTYLE_NORMAL
);
218 create_desc
.set_weight(PP_TRUETYPEFONTWEIGHT_NORMAL
);
219 pp::TrueTypeFont_Dev
font(instance_
, create_desc
);
220 // Describe: See what font-matching did with a generic font. We should always
221 // be able to Create a generic Serif font.
222 TestCompletionCallbackWithOutput
<pp::TrueTypeFontDesc_Dev
> cc(
223 instance_
->pp_instance(), false);
224 cc
.WaitForResult(font
.Describe(cc
.GetCallback()));
225 const pp::TrueTypeFontDesc_Dev desc
= cc
.output();
226 ASSERT_NE(0, desc
.family().AsString().size());
227 ASSERT_EQ(PP_TRUETYPEFONTFAMILY_SERIF
, desc
.generic_family());
228 ASSERT_EQ(PP_TRUETYPEFONTSTYLE_NORMAL
, desc
.style());
229 ASSERT_EQ(PP_TRUETYPEFONTWEIGHT_NORMAL
, desc
.weight());
231 // Describe an invalid resource should fail.
232 PP_TrueTypeFontDesc_Dev fail_desc
;
233 memset(&fail_desc
, 0, sizeof(fail_desc
));
234 fail_desc
.family
= PP_MakeUndefined();
235 // Create a shallow copy to check that no data is changed.
236 PP_TrueTypeFontDesc_Dev fail_desc_copy
;
237 memcpy(&fail_desc_copy
, &fail_desc
, sizeof(fail_desc
));
240 ppb_truetype_font_interface_
->Describe(
243 cc
.GetCallback().pp_completion_callback()));
244 ASSERT_EQ(PP_ERROR_BADRESOURCE
, cc
.result());
245 ASSERT_EQ(PP_VARTYPE_UNDEFINED
, fail_desc
.family
.type
);
246 ASSERT_EQ(0, memcmp(&fail_desc
, &fail_desc_copy
, sizeof(fail_desc
)));
251 std::string
TestTrueTypeFont::TestGetTableTags() {
252 pp::TrueTypeFontDesc_Dev desc
;
253 pp::TrueTypeFont_Dev
font(instance_
, desc
);
255 TestCompletionCallbackWithOutput
< std::vector
<uint32_t> > cc(
256 instance_
->pp_instance(), false);
257 cc
.WaitForResult(font
.GetTableTags(cc
.GetCallback()));
258 std::vector
<uint32_t> tags
= cc
.output();
259 ASSERT_NE(0, tags
.size());
260 ASSERT_EQ(static_cast<int32_t>(tags
.size()), cc
.result());
261 // Tags will vary depending on the actual font that the host platform
262 // chooses. Check that all required TrueType tags are present.
263 const int required_tag_count
= 9;
264 uint32_t required_tags
[required_tag_count
] = {
265 // Note: these must be sorted for std::includes below.
266 MAKE_TABLE_TAG('c', 'm', 'a', 'p'),
267 MAKE_TABLE_TAG('g', 'l', 'y', 'f'),
268 MAKE_TABLE_TAG('h', 'e', 'a', 'd'),
269 MAKE_TABLE_TAG('h', 'h', 'e', 'a'),
270 MAKE_TABLE_TAG('h', 'm', 't', 'x'),
271 MAKE_TABLE_TAG('l', 'o', 'c', 'a'),
272 MAKE_TABLE_TAG('m', 'a', 'x', 'p'),
273 MAKE_TABLE_TAG('n', 'a', 'm', 'e'),
274 MAKE_TABLE_TAG('p', 'o', 's', 't')
276 std::sort(tags
.begin(), tags
.end());
277 ASSERT_TRUE(std::includes(tags
.begin(),
280 required_tags
+ required_tag_count
));
283 // Invalid resource should fail and write no data.
284 TestCompletionCallbackWithOutput
< std::vector
<uint32_t> > cc(
285 instance_
->pp_instance(), false);
287 ppb_truetype_font_interface_
->GetTableTags(
289 cc
.GetCallback().output(),
290 cc
.GetCallback().pp_completion_callback()));
291 ASSERT_EQ(PP_ERROR_BADRESOURCE
, cc
.result());
292 ASSERT_EQ(0, cc
.output().size());
298 std::string
TestTrueTypeFont::TestGetTable() {
299 pp::TrueTypeFontDesc_Dev desc
;
300 pp::TrueTypeFont_Dev
font(instance_
, desc
);
303 // Getting a required table from a valid font should succeed.
304 TestCompletionCallbackWithOutput
< std::vector
<char> > cc1(
305 instance_
->pp_instance(), false);
306 cc1
.WaitForResult(font
.GetTable(MAKE_TABLE_TAG('c', 'm', 'a', 'p'),
307 0, std::numeric_limits
<int32_t>::max(),
309 const std::vector
<char> cmap_data
= cc1
.output();
310 ASSERT_NE(0, cmap_data
.size());
311 ASSERT_EQ(static_cast<int32_t>(cmap_data
.size()), cc1
.result());
313 // Passing 0 for the table tag should return the entire font.
314 TestCompletionCallbackWithOutput
< std::vector
<char> > cc2(
315 instance_
->pp_instance(), false);
316 cc2
.WaitForResult(font
.GetTable(0 /* table_tag */,
317 0, std::numeric_limits
<int32_t>::max(),
319 const std::vector
<char> entire_font
= cc2
.output();
320 ASSERT_NE(0, entire_font
.size());
321 ASSERT_EQ(static_cast<int32_t>(entire_font
.size()), cc2
.result());
323 // Verify that the CMAP table is in entire_font, and that it's identical
324 // to the one we retrieved above. Note that since the font header and table
325 // directory are in file (big-endian) order, we need to byte swap tags and
327 const size_t kHeaderSize
= sizeof(FontHeader
);
328 const size_t kEntrySize
= sizeof(FontDirectoryEntry
);
329 ASSERT_TRUE(kHeaderSize
< entire_font
.size());
331 memcpy(&header
, &entire_font
[0], kHeaderSize
);
332 uint16_t num_tables
= ReadBigEndian16(&header
.num_tables
);
333 std::vector
<FontDirectoryEntry
> directory(num_tables
);
334 size_t directory_size
= kEntrySize
* num_tables
;
335 ASSERT_TRUE(kHeaderSize
+ directory_size
< entire_font
.size());
336 memcpy(&directory
[0], &entire_font
[kHeaderSize
], directory_size
);
337 const FontDirectoryEntry
* cmap_entry
= NULL
;
338 for (uint16_t i
= 0; i
< num_tables
; i
++) {
339 if (ReadBigEndian32(&directory
[i
].tag
) ==
340 MAKE_TABLE_TAG('c', 'm', 'a', 'p')) {
341 cmap_entry
= &directory
[i
];
345 ASSERT_NE(NULL
, cmap_entry
);
347 uint32_t logical_length
= ReadBigEndian32(&cmap_entry
->logical_length
);
348 uint32_t table_offset
= ReadBigEndian32(&cmap_entry
->offset
);
349 ASSERT_EQ(static_cast<size_t>(logical_length
), cmap_data
.size());
350 ASSERT_TRUE(static_cast<size_t>(table_offset
+ logical_length
) <
352 const char* cmap_table
= &entire_font
[0] + table_offset
;
353 ASSERT_EQ(0, memcmp(cmap_table
, &cmap_data
[0], cmap_data
.size()));
355 // Use offset and max_data_length to restrict the data. Read a part of
357 TestCompletionCallbackWithOutput
< std::vector
<char> > cc3(
358 instance_
->pp_instance(), false);
359 const int32_t kOffset
= 4;
360 int32_t partial_cmap_size
= static_cast<int32_t>(cmap_data
.size() - 64);
361 cc3
.WaitForResult(font
.GetTable(MAKE_TABLE_TAG('c', 'm', 'a', 'p'),
365 const std::vector
<char> partial_cmap_data
= cc3
.output();
366 ASSERT_EQ(partial_cmap_data
.size(), static_cast<size_t>(cc3
.result()));
367 ASSERT_EQ(partial_cmap_data
.size(), static_cast<size_t>(partial_cmap_size
));
368 ASSERT_EQ(0, memcmp(cmap_table
+ kOffset
, &partial_cmap_data
[0],
372 // Getting an invalid table should fail ('zzzz' should be safely invalid).
373 TestCompletionCallbackWithOutput
< std::vector
<char> > cc(
374 instance_
->pp_instance(), false);
375 cc
.WaitForResult(font
.GetTable(MAKE_TABLE_TAG('z', 'z', 'z', 'z'),
376 0, std::numeric_limits
<int32_t>::max(),
378 ASSERT_EQ(0, cc
.output().size());
379 ASSERT_EQ(PP_ERROR_FAILED
, cc
.result());
382 // GetTable on an invalid resource should fail with a bad resource error
383 // and write no data.
384 TestCompletionCallbackWithOutput
< std::vector
<char> > cc(
385 instance_
->pp_instance(), false);
387 ppb_truetype_font_interface_
->GetTable(
389 MAKE_TABLE_TAG('c', 'm', 'a', 'p'),
390 0, std::numeric_limits
<int32_t>::max(),
391 cc
.GetCallback().output(),
392 cc
.GetCallback().pp_completion_callback()));
393 ASSERT_EQ(PP_ERROR_BADRESOURCE
, cc
.result());
394 ASSERT_EQ(0, cc
.output().size());
397 // Negative offset should fail with a bad argument error and write no data.
398 TestCompletionCallbackWithOutput
< std::vector
<char> > cc(
399 instance_
->pp_instance(), false);
400 cc
.WaitForResult(font
.GetTable(MAKE_TABLE_TAG('c', 'm', 'a', 'p'),
403 ASSERT_EQ(PP_ERROR_BADARGUMENT
, cc
.result());
404 ASSERT_EQ(0, cc
.output().size());
407 // Offset larger than file size succeeds but returns no data.
408 TestCompletionCallbackWithOutput
< std::vector
<char> > cc(
409 instance_
->pp_instance(), false);
410 cc
.WaitForResult(font
.GetTable(MAKE_TABLE_TAG('c', 'm', 'a', 'p'),
413 ASSERT_EQ(PP_OK
, cc
.result());
414 ASSERT_EQ(0, cc
.output().size());
417 // Negative max_data_length should fail with a bad argument error and write
419 TestCompletionCallbackWithOutput
< std::vector
<char> > cc(
420 instance_
->pp_instance(), false);
421 cc
.WaitForResult(font
.GetTable(MAKE_TABLE_TAG('c', 'm', 'a', 'p'),
424 ASSERT_EQ(PP_ERROR_BADARGUMENT
, cc
.result());
425 ASSERT_EQ(0, cc
.output().size());