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. 4 5#include "content/browser/renderer_host/pepper/pepper_truetype_font.h" 6 7#import <ApplicationServices/ApplicationServices.h> 8 9#include <stdio.h> 10 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" 20 21namespace content { 22 23namespace { 24 25static bool FindFloat(CFDictionaryRef dict, CFStringRef name, float* value) { 26 CFNumberRef num; 27 return CFDictionaryGetValueIfPresent( 28 dict, name, reinterpret_cast<const void**>(&num)) && 29 CFNumberIsFloatType(num) && 30 CFNumberGetValue(num, kCFNumberFloatType, value); 31} 32 33float 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); 39} 40 41PP_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); 46} 47 48float 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); 54} 55 56PP_TrueTypeFontWidth_Dev GetPepperWidth(float width) { 57 // Perform the inverse mapping of GetMacWeight. 58 return static_cast<PP_TrueTypeFontWidth_Dev>( 59 width * 60 (PP_TRUETYPEFONTWIDTH_ULTRAEXPANDED - PP_TRUETYPEFONTWIDTH_NORMAL) + 61 PP_TRUETYPEFONTWIDTH_NORMAL); 62} 63 64#define MAKE_TABLE_TAG(a, b, c, d) ((a) << 24) + ((b) << 16) + ((c) << 8) + (d) 65 66// TrueType font header and table entry structs. See 67// https://developer.apple.com/fonts/TTRefMan/RM06/Chap6.html 68struct FontHeader { 69 int32_t font_type; 70 uint16_t num_tables; 71 uint16_t search_range; 72 uint16_t entry_selector; 73 uint16_t range_shift; 74}; 75static_assert(sizeof(FontHeader) == 12, "FontHeader wrong size"); 76 77struct FontDirectoryEntry { 78 uint32_t tag; 79 uint32_t checksum; 80 uint32_t offset; 81 uint32_t logical_length; 82}; 83static_assert(sizeof(FontDirectoryEntry) == 16, 84 "FontDirectoryEntry wrong size"); 85 86uint32_t CalculateChecksum(char* table, int32_t table_length) { 87 uint32_t sum = 0; 88 uint32_t* current = reinterpret_cast<uint32_t*>(table); 89 uint32_t length = (table_length + 3) / 4; 90 // Raw font data is big-endian. 91 while (length-- > 0) 92 sum += base::NetToHost32(*current++); 93 return sum; 94} 95 96class PepperTrueTypeFontMac : public PepperTrueTypeFont { 97 public: 98 PepperTrueTypeFontMac(); 99 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, 105 int32_t offset, 106 int32_t max_data_length, 107 std::string* data) OVERRIDE; 108 109 private: 110 virtual ~PepperTrueTypeFontMac() OVERRIDE; 111 112 virtual int32_t GetEntireFont(int32_t offset, 113 int32_t max_data_length, 114 std::string* data); 115 116 base::ScopedCFTypeRef<CTFontRef> font_ref_; 117 118 DISALLOW_COPY_AND_ASSIGN(PepperTrueTypeFontMac); 119}; 120 121PepperTrueTypeFontMac::PepperTrueTypeFontMac() { 122} 123 124PepperTrueTypeFontMac::~PepperTrueTypeFontMac() { 125} 126 127int32_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. 131 { 132 // Create attributes and traits dictionaries. 133 base::ScopedCFTypeRef<CFMutableDictionaryRef> attributes_ref( 134 CFDictionaryCreateMutable(kCFAllocatorDefault, 135 0, 136 &kCFTypeDictionaryKeyCallBacks, 137 &kCFTypeDictionaryValueCallBacks)); 138 139 base::ScopedCFTypeRef<CFMutableDictionaryRef> traits_ref( 140 CFDictionaryCreateMutable(kCFAllocatorDefault, 141 0, 142 &kCFTypeDictionaryKeyCallBacks, 143 &kCFTypeDictionaryValueCallBacks)); 144 if (!attributes_ref || !traits_ref) 145 return PP_ERROR_FAILED; 146 147 CFDictionaryAddValue(attributes_ref, kCTFontTraitsAttribute, traits_ref); 148 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; 159 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); 165 166 // Font family matching doesn't work using family classes in symbolic 167 // traits. Instead, map generic_family to font families that are always 168 // available. 169 std::string family(desc->family); 170 if (family.empty()) { 171 switch (desc->generic_family) { 172 case PP_TRUETYPEFONTFAMILY_SERIF: 173 family = "Times"; 174 break; 175 case PP_TRUETYPEFONTFAMILY_SANSSERIF: 176 family = "Helvetica"; 177 break; 178 case PP_TRUETYPEFONTFAMILY_CURSIVE: 179 family = "Apple Chancery"; 180 break; 181 case PP_TRUETYPEFONTFAMILY_FANTASY: 182 family = "Papyrus"; 183 break; 184 case PP_TRUETYPEFONTFAMILY_MONOSPACE: 185 family = "Courier"; 186 break; 187 } 188 } 189 190 base::ScopedCFTypeRef<CFStringRef> name_ref( 191 base::SysUTF8ToCFStringRef(family)); 192 if (name_ref) 193 CFDictionaryAddValue( 194 attributes_ref, kCTFontFamilyNameAttribute, name_ref); 195 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); 203 } 204 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)); 211 if (width_trait_ref) 212 CFDictionaryAddValue(traits_ref, kCTFontWidthTrait, width_trait_ref); 213 } 214 215 base::ScopedCFTypeRef<CTFontDescriptorRef> desc_ref( 216 CTFontDescriptorCreateWithAttributes(attributes_ref)); 217 218 if (desc_ref) 219 font_ref_.reset(CTFontCreateWithFontDescriptor(desc_ref, 0, NULL)); 220 221 if (!font_ref_.get()) 222 return PP_ERROR_FAILED; 223 } 224 225 // Now query to get the actual font characteristics. 226 base::ScopedCFTypeRef<CTFontDescriptorRef> desc_ref( 227 CTFontCopyFontDescriptor(font_ref_)); 228 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); 233 234 base::ScopedCFTypeRef<CFDictionaryRef> traits_ref( 235 base::mac::CFCast<CFDictionaryRef>( 236 CTFontDescriptorCopyAttribute(desc_ref, kCTFontTraitsAttribute))); 237 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; 245 } else { 246 float weight; 247 if (FindFloat(traits_ref, kCTFontWeightTrait, &weight)) 248 desc->weight = GetPepperWeight(weight); 249 } 250 if (symbolic_traits & kCTFontCondensedTrait) { 251 desc->width = PP_TRUETYPEFONTWIDTH_CONDENSED; 252 } else if (symbolic_traits & kCTFontExpandedTrait) { 253 desc->width = PP_TRUETYPEFONTWIDTH_EXPANDED; 254 } else { 255 float width; 256 if (FindFloat(traits_ref, kCTFontWidthTrait, &width)) 257 desc->width = GetPepperWidth(width); 258 } 259 260 // Character set isn't supported on Mac. 261 desc->charset = PP_TRUETYPEFONTCHARSET_DEFAULT; 262 return PP_OK; 263} 264 265int32_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)); 270 if (!tag_array) 271 return PP_ERROR_FAILED; 272 273 // Items returned by CTFontCopyAvailableTables are not boxed. Whose bright 274 // idea was this? 275 CFIndex length = CFArrayGetCount(tag_array); 276 tags->resize(length); 277 for (CFIndex i = 0; i < length; ++i) { 278 (*tags)[i] = 279 reinterpret_cast<uintptr_t>(CFArrayGetValueAtIndex(tag_array, i)); 280 } 281 return length; 282} 283 284int32_t PepperTrueTypeFontMac::GetTable(uint32_t table_tag, 285 int32_t offset, 286 int32_t max_data_length, 287 std::string* data) { 288 if (!font_ref_.get()) 289 return PP_ERROR_FAILED; 290 291 if (!table_tag) 292 return GetEntireFont(offset, max_data_length, data); 293 294 base::ScopedCFTypeRef<CFDataRef> table_ref( 295 CTFontCopyTable(font_ref_, 296 static_cast<CTFontTableTag>(table_tag), 297 kCTFontTableOptionNoOptions)); 298 if (!table_ref) 299 return PP_ERROR_FAILED; 300 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])); 310 311 return safe_length; 312} 313 314int32_t PepperTrueTypeFontMac::GetEntireFont(int32_t offset, 315 int32_t max_data_length, 316 std::string* data) { 317 // Reconstruct the font header, table directory, and tables. 318 std::vector<uint32_t> table_tags; 319 int32_t table_count = GetTableTags(&table_tags); 320 if (table_count < 0) 321 return table_count; // PPAPI error code. 322 323 // Allocate enough room for the header and the table directory entries. 324 std::string font( 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))); 330 int32_t font_type; 331 CFNumberGetValue(font_type_ref, kCFNumberSInt32Type, &font_type); 332 switch (font_type) { 333 case kCTFontFormatOpenTypePostScript: 334 font_type = MAKE_TABLE_TAG('O', 'T', 'T', 'O'); 335 break; 336 case kCTFontFormatTrueType: 337 case kCTFontFormatBitmap: 338 font_type = MAKE_TABLE_TAG('t', 'r', 'u', 'e'); 339 break; 340 case kCTFontFormatPostScript: 341 font_type = MAKE_TABLE_TAG('t', 'y', 'p', '1'); 342 break; 343 case kCTFontFormatOpenTypeTrueType: 344 case kCTFontFormatUnrecognized: 345 default: 346 font_type = MAKE_TABLE_TAG(0, 1, 0, 0); 347 break; 348 } 349 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)) { 355 entry_selector++; 356 search_range <<= 1; 357 } 358 search_range <<= 4; 359 uint16_t range_shift = (num_tables << 4) - search_range; 360 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); 368 369 for (int32_t i = 0; i < table_count; i++) { 370 // Get the table data. 371 std::string table; 372 int32_t table_size = 373 GetTable(table_tags[i], 0, std::numeric_limits<int32_t>::max(), &table); 374 if (table_size < 0) 375 return table_size; // PPAPI error code. 376 377 // Append it to the font data so far, and zero pad so tables stay aligned. 378 size_t table_offset = font.size(); 379 font.append(table); 380 size_t padding = font.size() & 0x3; 381 font.append(padding, 0); 382 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]); 387 entry->checksum = 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. 392 } 393 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); 400 401 data->clear(); 402 data->swap(font); 403 return safe_length; 404} 405 406} // namespace 407 408// static 409PepperTrueTypeFont* PepperTrueTypeFont::Create() { 410 return new PepperTrueTypeFontMac(); 411} 412 413} // namespace content 414