1 // Copyright 2014 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 #include "content/browser/renderer_host/pepper/pepper_truetype_font.h"
7 #import <ApplicationServices/ApplicationServices.h>
11 #include "base/compiler_specific.h"
12 #include "base/mac/foundation_util.h"
13 #include "base/mac/scoped_cftyperef.h"
14 #include "base/mac/scoped_nsautorelease_pool.h"
15 #include "base/numerics/safe_conversions.h"
16 #include "base/strings/sys_string_conversions.h"
17 #include "base/sys_byteorder.h"
18 #include "ppapi/c/dev/ppb_truetype_font_dev.h"
19 #include "ppapi/c/pp_errors.h"
25 static bool FindFloat(CFDictionaryRef dict, CFStringRef name, float* value) {
27 return CFDictionaryGetValueIfPresent(
28 dict, name, reinterpret_cast<const void**>(&num)) &&
29 CFNumberIsFloatType(num) &&
30 CFNumberGetValue(num, kCFNumberFloatType, value);
33 float GetMacWeight(PP_TrueTypeFontWeight_Dev weight) {
34 // Map values from NORMAL (400) to HEAVY (900) to the range [0 .. 1], and
35 // values below NORMAL to the range [-0.6 .. 0]. NORMAL should map to 0.
36 float normal = PP_TRUETYPEFONTWEIGHT_NORMAL;
37 float heavy = PP_TRUETYPEFONTWEIGHT_HEAVY;
38 return (weight - normal) / (heavy - normal);
41 PP_TrueTypeFontWeight_Dev GetPepperWeight(float weight) {
42 // Perform the inverse mapping of GetMacWeight.
43 return static_cast<PP_TrueTypeFontWeight_Dev>(
44 weight * (PP_TRUETYPEFONTWEIGHT_HEAVY - PP_TRUETYPEFONTWEIGHT_NORMAL) +
45 PP_TRUETYPEFONTWEIGHT_NORMAL);
48 float GetMacWidth(PP_TrueTypeFontWidth_Dev width) {
49 // Map values from NORMAL (4) to ULTRA_EXPANDED (8) to the range [0 .. 1],
50 // and values below NORMAL to the range [-1 .. 0]. Normal should map to 0.
51 float normal = PP_TRUETYPEFONTWIDTH_NORMAL;
52 float ultra_expanded = PP_TRUETYPEFONTWIDTH_ULTRAEXPANDED;
53 return (width - normal) / (ultra_expanded - normal);
56 PP_TrueTypeFontWidth_Dev GetPepperWidth(float width) {
57 // Perform the inverse mapping of GetMacWeight.
58 return static_cast<PP_TrueTypeFontWidth_Dev>(
60 (PP_TRUETYPEFONTWIDTH_ULTRAEXPANDED - PP_TRUETYPEFONTWIDTH_NORMAL) +
61 PP_TRUETYPEFONTWIDTH_NORMAL);
64 #define MAKE_TABLE_TAG(a, b, c, d) ((a) << 24) + ((b) << 16) + ((c) << 8) + (d)
66 // TrueType font header and table entry structs. See
67 // https://developer.apple.com/fonts/TTRefMan/RM06/Chap6.html
71 uint16_t search_range;
72 uint16_t entry_selector;
75 static_assert(sizeof(FontHeader) == 12, "FontHeader wrong size");
77 struct FontDirectoryEntry {
81 uint32_t logical_length;
83 static_assert(sizeof(FontDirectoryEntry) == 16,
84 "FontDirectoryEntry wrong size");
86 uint32_t CalculateChecksum(char* table, int32_t table_length) {
88 uint32_t* current = reinterpret_cast<uint32_t*>(table);
89 uint32_t length = (table_length + 3) / 4;
90 // Raw font data is big-endian.
92 sum += base::NetToHost32(*current++);
96 class PepperTrueTypeFontMac : public PepperTrueTypeFont {
98 PepperTrueTypeFontMac();
100 // PepperTrueTypeFont implementation.
101 virtual int32_t Initialize(
102 ppapi::proxy::SerializedTrueTypeFontDesc* desc) OVERRIDE;
103 virtual int32_t GetTableTags(std::vector<uint32_t>* tags) OVERRIDE;
104 virtual int32_t GetTable(uint32_t table_tag,
106 int32_t max_data_length,
107 std::string* data) OVERRIDE;
110 virtual ~PepperTrueTypeFontMac() OVERRIDE;
112 virtual int32_t GetEntireFont(int32_t offset,
113 int32_t max_data_length,
116 base::ScopedCFTypeRef<CTFontRef> font_ref_;
118 DISALLOW_COPY_AND_ASSIGN(PepperTrueTypeFontMac);
121 PepperTrueTypeFontMac::PepperTrueTypeFontMac() {
124 PepperTrueTypeFontMac::~PepperTrueTypeFontMac() {
127 int32_t PepperTrueTypeFontMac::Initialize(
128 ppapi::proxy::SerializedTrueTypeFontDesc* desc) {
129 // Create the font in a nested scope, so we can use the same variable names
130 // when we get the actual font characteristics.
132 // Create attributes and traits dictionaries.
133 base::ScopedCFTypeRef<CFMutableDictionaryRef> attributes_ref(
134 CFDictionaryCreateMutable(kCFAllocatorDefault,
136 &kCFTypeDictionaryKeyCallBacks,
137 &kCFTypeDictionaryValueCallBacks));
139 base::ScopedCFTypeRef<CFMutableDictionaryRef> traits_ref(
140 CFDictionaryCreateMutable(kCFAllocatorDefault,
142 &kCFTypeDictionaryKeyCallBacks,
143 &kCFTypeDictionaryValueCallBacks));
144 if (!attributes_ref || !traits_ref)
145 return PP_ERROR_FAILED;
147 CFDictionaryAddValue(attributes_ref, kCTFontTraitsAttribute, traits_ref);
149 // Use symbolic traits to specify traits when possible.
150 CTFontSymbolicTraits symbolic_traits = 0;
151 if (desc->style & PP_TRUETYPEFONTSTYLE_ITALIC)
152 symbolic_traits |= kCTFontItalicTrait;
153 if (desc->weight == PP_TRUETYPEFONTWEIGHT_BOLD)
154 symbolic_traits |= kCTFontBoldTrait;
155 if (desc->width == PP_TRUETYPEFONTWIDTH_CONDENSED)
156 symbolic_traits |= kCTFontCondensedTrait;
157 else if (desc->width == PP_TRUETYPEFONTWIDTH_EXPANDED)
158 symbolic_traits |= kCTFontExpandedTrait;
160 base::ScopedCFTypeRef<CFNumberRef> symbolic_traits_ref(CFNumberCreate(
161 kCFAllocatorDefault, kCFNumberSInt32Type, &symbolic_traits));
162 if (!symbolic_traits_ref)
163 return PP_ERROR_FAILED;
164 CFDictionaryAddValue(traits_ref, kCTFontSymbolicTrait, symbolic_traits_ref);
166 // Font family matching doesn't work using family classes in symbolic
167 // traits. Instead, map generic_family to font families that are always
169 std::string family(desc->family);
170 if (family.empty()) {
171 switch (desc->generic_family) {
172 case PP_TRUETYPEFONTFAMILY_SERIF:
175 case PP_TRUETYPEFONTFAMILY_SANSSERIF:
176 family = "Helvetica";
178 case PP_TRUETYPEFONTFAMILY_CURSIVE:
179 family = "Apple Chancery";
181 case PP_TRUETYPEFONTFAMILY_FANTASY:
184 case PP_TRUETYPEFONTFAMILY_MONOSPACE:
190 base::ScopedCFTypeRef<CFStringRef> name_ref(
191 base::SysUTF8ToCFStringRef(family));
193 CFDictionaryAddValue(
194 attributes_ref, kCTFontFamilyNameAttribute, name_ref);
196 if (desc->weight != PP_TRUETYPEFONTWEIGHT_NORMAL &&
197 desc->weight != PP_TRUETYPEFONTWEIGHT_BOLD) {
198 float weight = GetMacWeight(desc->weight);
199 base::ScopedCFTypeRef<CFNumberRef> weight_trait_ref(
200 CFNumberCreate(kCFAllocatorDefault, kCFNumberFloat32Type, &weight));
201 if (weight_trait_ref)
202 CFDictionaryAddValue(traits_ref, kCTFontWeightTrait, weight_trait_ref);
205 if (desc->width != PP_TRUETYPEFONTWIDTH_NORMAL &&
206 desc->width != PP_TRUETYPEFONTWIDTH_CONDENSED &&
207 desc->width != PP_TRUETYPEFONTWIDTH_EXPANDED) {
208 float width = GetMacWidth(desc->width);
209 base::ScopedCFTypeRef<CFNumberRef> width_trait_ref(
210 CFNumberCreate(kCFAllocatorDefault, kCFNumberFloat32Type, &width));
212 CFDictionaryAddValue(traits_ref, kCTFontWidthTrait, width_trait_ref);
215 base::ScopedCFTypeRef<CTFontDescriptorRef> desc_ref(
216 CTFontDescriptorCreateWithAttributes(attributes_ref));
219 font_ref_.reset(CTFontCreateWithFontDescriptor(desc_ref, 0, NULL));
221 if (!font_ref_.get())
222 return PP_ERROR_FAILED;
225 // Now query to get the actual font characteristics.
226 base::ScopedCFTypeRef<CTFontDescriptorRef> desc_ref(
227 CTFontCopyFontDescriptor(font_ref_));
229 base::ScopedCFTypeRef<CFStringRef> family_name_ref(
230 base::mac::CFCast<CFStringRef>(
231 CTFontDescriptorCopyAttribute(desc_ref, kCTFontFamilyNameAttribute)));
232 desc->family = base::SysCFStringRefToUTF8(family_name_ref);
234 base::ScopedCFTypeRef<CFDictionaryRef> traits_ref(
235 base::mac::CFCast<CFDictionaryRef>(
236 CTFontDescriptorCopyAttribute(desc_ref, kCTFontTraitsAttribute)));
238 desc->style = PP_TRUETYPEFONTSTYLE_NORMAL;
239 CTFontSymbolicTraits symbolic_traits(CTFontGetSymbolicTraits(font_ref_));
240 if (symbolic_traits & kCTFontItalicTrait)
241 desc->style = static_cast<PP_TrueTypeFontStyle_Dev>(
242 desc->style | PP_TRUETYPEFONTSTYLE_ITALIC);
243 if (symbolic_traits & kCTFontBoldTrait) {
244 desc->weight = PP_TRUETYPEFONTWEIGHT_BOLD;
247 if (FindFloat(traits_ref, kCTFontWeightTrait, &weight))
248 desc->weight = GetPepperWeight(weight);
250 if (symbolic_traits & kCTFontCondensedTrait) {
251 desc->width = PP_TRUETYPEFONTWIDTH_CONDENSED;
252 } else if (symbolic_traits & kCTFontExpandedTrait) {
253 desc->width = PP_TRUETYPEFONTWIDTH_EXPANDED;
256 if (FindFloat(traits_ref, kCTFontWidthTrait, &width))
257 desc->width = GetPepperWidth(width);
260 // Character set isn't supported on Mac.
261 desc->charset = PP_TRUETYPEFONTCHARSET_DEFAULT;
265 int32_t PepperTrueTypeFontMac::GetTableTags(std::vector<uint32_t>* tags) {
266 if (!font_ref_.get())
267 return PP_ERROR_FAILED;
268 base::ScopedCFTypeRef<CFArrayRef> tag_array(
269 CTFontCopyAvailableTables(font_ref_, kCTFontTableOptionNoOptions));
271 return PP_ERROR_FAILED;
273 // Items returned by CTFontCopyAvailableTables are not boxed. Whose bright
275 CFIndex length = CFArrayGetCount(tag_array);
276 tags->resize(length);
277 for (CFIndex i = 0; i < length; ++i) {
279 reinterpret_cast<uintptr_t>(CFArrayGetValueAtIndex(tag_array, i));
284 int32_t PepperTrueTypeFontMac::GetTable(uint32_t table_tag,
286 int32_t max_data_length,
288 if (!font_ref_.get())
289 return PP_ERROR_FAILED;
292 return GetEntireFont(offset, max_data_length, data);
294 base::ScopedCFTypeRef<CFDataRef> table_ref(
295 CTFontCopyTable(font_ref_,
296 static_cast<CTFontTableTag>(table_tag),
297 kCTFontTableOptionNoOptions));
299 return PP_ERROR_FAILED;
301 CFIndex table_size = CFDataGetLength(table_ref);
302 CFIndex safe_offset =
303 std::min(base::checked_cast<CFIndex>(offset), table_size);
304 CFIndex safe_length = std::min(table_size - safe_offset,
305 base::checked_cast<CFIndex>(max_data_length));
306 data->resize(safe_length);
307 CFDataGetBytes(table_ref,
308 CFRangeMake(safe_offset, safe_length),
309 reinterpret_cast<UInt8*>(&(*data)[0]));
314 int32_t PepperTrueTypeFontMac::GetEntireFont(int32_t offset,
315 int32_t max_data_length,
317 // Reconstruct the font header, table directory, and tables.
318 std::vector<uint32_t> table_tags;
319 int32_t table_count = GetTableTags(&table_tags);
321 return table_count; // PPAPI error code.
323 // Allocate enough room for the header and the table directory entries.
325 sizeof(FontHeader) + sizeof(FontDirectoryEntry) * table_count, 0);
326 // Map the OS X font type value to a TrueType scalar type.
327 base::ScopedCFTypeRef<CFNumberRef> font_type_ref(
328 base::mac::CFCast<CFNumberRef>(
329 CTFontCopyAttribute(font_ref_, kCTFontFormatAttribute)));
331 CFNumberGetValue(font_type_ref, kCFNumberSInt32Type, &font_type);
333 case kCTFontFormatOpenTypePostScript:
334 font_type = MAKE_TABLE_TAG('O', 'T', 'T', 'O');
336 case kCTFontFormatTrueType:
337 case kCTFontFormatBitmap:
338 font_type = MAKE_TABLE_TAG('t', 'r', 'u', 'e');
340 case kCTFontFormatPostScript:
341 font_type = MAKE_TABLE_TAG('t', 'y', 'p', '1');
343 case kCTFontFormatOpenTypeTrueType:
344 case kCTFontFormatUnrecognized:
346 font_type = MAKE_TABLE_TAG(0, 1, 0, 0);
350 // Calculate the rest of the header values.
351 uint16_t num_tables = base::checked_cast<uint16_t>(table_count);
352 uint16_t entry_selector = 0;
353 uint16_t search_range = 1;
354 while (search_range < (num_tables >> 1)) {
359 uint16_t range_shift = (num_tables << 4) - search_range;
361 // Write the header, with values in big-endian order.
362 FontHeader* font_header = reinterpret_cast<FontHeader*>(&font[0]);
363 font_header->font_type = base::HostToNet32(font_type);
364 font_header->num_tables = base::HostToNet16(num_tables);
365 font_header->search_range = base::HostToNet16(search_range);
366 font_header->entry_selector = base::HostToNet16(entry_selector);
367 font_header->range_shift = base::HostToNet16(range_shift);
369 for (int32_t i = 0; i < table_count; i++) {
370 // Get the table data.
373 GetTable(table_tags[i], 0, std::numeric_limits<int32_t>::max(), &table);
375 return table_size; // PPAPI error code.
377 // Append it to the font data so far, and zero pad so tables stay aligned.
378 size_t table_offset = font.size();
380 size_t padding = font.size() & 0x3;
381 font.append(padding, 0);
383 // Fill in the directory entry for this table.
384 FontDirectoryEntry* entry = reinterpret_cast<FontDirectoryEntry*>(
385 &font[0] + sizeof(FontHeader) + i * sizeof(FontDirectoryEntry));
386 entry->tag = base::HostToNet32(table_tags[i]);
388 base::HostToNet32(CalculateChecksum(&font[table_offset], table_size));
389 entry->offset = base::HostToNet32(table_offset);
390 entry->logical_length = base::HostToNet32(table_size);
391 // TODO(bbudge) set the 'head' table checksumAdjustment.
394 // Extract a substring if the caller specified an offset or max data length.
395 int32_t font_size = base::checked_cast<int32_t>(font.size());
396 int32_t safe_offset = std::min(offset, font_size);
397 int32_t safe_length = std::min(font_size - safe_offset, max_data_length);
398 if (safe_offset || safe_length != font_size)
399 font = font.substr(safe_offset, safe_length);
409 PepperTrueTypeFont* PepperTrueTypeFont::Create() {
410 return new PepperTrueTypeFontMac();
413 } // namespace content