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/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)
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 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");
83 ppb_truetype_font_interface_
&&
84 ppb_core_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_
,
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);
119 ppb_truetype_font_interface_
->GetFontFamilies(
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());
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_
,
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(
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");
179 ppb_truetype_font_interface_
->GetFontsInFamily(
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());
192 std::string
TestTrueTypeFont::TestCreate() {
194 PP_TrueTypeFontDesc_Dev desc
= {
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
));
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
));
245 ppb_truetype_font_interface_
->Describe(
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
)));
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(),
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);
292 ppb_truetype_font_interface_
->GetTableTags(
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());
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(),
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(),
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
332 const size_t kHeaderSize
= sizeof(FontHeader
);
333 const size_t kEntrySize
= sizeof(FontDirectoryEntry
);
334 ASSERT_TRUE(kHeaderSize
< entire_font
.size());
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
];
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
) <
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
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'),
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],
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(),
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);
392 ppb_truetype_font_interface_
->GetTable(
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'),
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'),
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
424 TestCompletionCallbackWithOutput
< std::vector
<char> > cc(
425 instance_
->pp_instance(), false);
426 cc
.WaitForResult(font
.GetTable(MAKE_TABLE_TAG('c', 'm', 'a', 'p'),
429 ASSERT_EQ(PP_ERROR_BADARGUMENT
, cc
.result());
430 ASSERT_EQ(0, cc
.output().size());