- add sources.
[platform/framework/web/crosswalk.git] / src / content / renderer / pepper / pepper_truetype_font_mac.mm
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 #include "content/renderer/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/safe_numerics.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
21 namespace content {
22
23 namespace {
24
25 static bool FindFloat(CFDictionaryRef dict, CFStringRef name, float* value) {
26   CFNumberRef num;
27   return
28       CFDictionaryGetValueIfPresent(dict, name,
29                                     reinterpret_cast<const void**>(&num)) &&
30       CFNumberIsFloatType(num) &&
31       CFNumberGetValue(num, kCFNumberFloatType, value);
32 }
33
34 float GetMacWeight(PP_TrueTypeFontWeight_Dev weight) {
35   // Map values from NORMAL (400) to HEAVY (900) to the range [0 .. 1], and
36   // values below NORMAL to the range [-0.6 .. 0]. NORMAL should map to 0.
37   float normal = PP_TRUETYPEFONTWEIGHT_NORMAL;
38   float heavy = PP_TRUETYPEFONTWEIGHT_HEAVY;
39   return (weight - normal) / (heavy - normal);
40 }
41
42 PP_TrueTypeFontWeight_Dev GetPepperWeight(float weight) {
43   // Perform the inverse mapping of GetMacWeight.
44   return static_cast<PP_TrueTypeFontWeight_Dev>(
45       weight * (PP_TRUETYPEFONTWEIGHT_HEAVY - PP_TRUETYPEFONTWEIGHT_NORMAL) +
46       PP_TRUETYPEFONTWEIGHT_NORMAL);
47 }
48
49 float GetMacWidth(PP_TrueTypeFontWidth_Dev width) {
50   // Map values from NORMAL (4) to ULTRA_EXPANDED (8) to the range [0 .. 1],
51   // and values below NORMAL to the range [-1 .. 0]. Normal should map to 0.
52   float normal = PP_TRUETYPEFONTWIDTH_NORMAL;
53   float ultra_expanded = PP_TRUETYPEFONTWIDTH_ULTRAEXPANDED;
54   return (width - normal) / (ultra_expanded - normal);
55 }
56
57 PP_TrueTypeFontWidth_Dev GetPepperWidth(float width) {
58   // Perform the inverse mapping of GetMacWeight.
59   return static_cast<PP_TrueTypeFontWidth_Dev>(
60       width *
61       (PP_TRUETYPEFONTWIDTH_ULTRAEXPANDED - PP_TRUETYPEFONTWIDTH_NORMAL) +
62       PP_TRUETYPEFONTWIDTH_NORMAL);
63 }
64
65 #define MAKE_TABLE_TAG(a, b, c, d) ((a) << 24) + ((b) << 16) + ((c) << 8) + (d)
66
67 // TrueType font header and table entry structs. See
68 // https://developer.apple.com/fonts/TTRefMan/RM06/Chap6.html
69 struct FontHeader {
70   int32_t font_type;
71   uint16_t num_tables;
72   uint16_t search_range;
73   uint16_t entry_selector;
74   uint16_t range_shift;
75 };
76 static_assert(sizeof(FontHeader) == 12, "FontHeader wrong size");
77
78 struct FontDirectoryEntry {
79   uint32_t tag;
80   uint32_t checksum;
81   uint32_t offset;
82   uint32_t logical_length;
83 };
84 static_assert(sizeof(FontDirectoryEntry) == 16,
85               "FontDirectoryEntry wrong size");
86
87 uint32_t CalculateChecksum(char* table, int32_t table_length) {
88   uint32_t sum = 0;
89   uint32_t* current = reinterpret_cast<uint32_t*>(table);
90   uint32_t length = (table_length + 3) / 4;
91   // Raw font data is big-endian.
92   while (length-- > 0)
93     sum += base::NetToHost32(*current++);
94   return sum;
95 }
96
97 class PepperTrueTypeFontMac : public PepperTrueTypeFont {
98  public:
99   explicit PepperTrueTypeFontMac(
100       const ppapi::proxy::SerializedTrueTypeFontDesc& desc);
101   virtual ~PepperTrueTypeFontMac() OVERRIDE;
102
103   // PepperTrueTypeFont overrides.
104   virtual bool IsValid() OVERRIDE;
105   virtual int32_t Describe(
106       ppapi::proxy::SerializedTrueTypeFontDesc* desc) OVERRIDE;
107   virtual int32_t GetTableTags(std::vector<uint32_t>* tags) OVERRIDE;
108   virtual int32_t GetTable(uint32_t table_tag,
109                            int32_t offset,
110                            int32_t max_data_length,
111                            std::string* data) OVERRIDE;
112  private:
113   virtual int32_t GetEntireFont(int32_t offset,
114                                 int32_t max_data_length,
115                                 std::string* data);
116
117   base::ScopedCFTypeRef<CTFontRef> font_ref_;
118
119   DISALLOW_COPY_AND_ASSIGN(PepperTrueTypeFontMac);
120 };
121
122 PepperTrueTypeFontMac::PepperTrueTypeFontMac(
123     const ppapi::proxy::SerializedTrueTypeFontDesc& desc) {
124   // Create attributes and traits dictionaries.
125   base::ScopedCFTypeRef<CFMutableDictionaryRef> attributes_ref(
126       CFDictionaryCreateMutable(kCFAllocatorDefault,
127                                 0,
128                                 &kCFTypeDictionaryKeyCallBacks,
129                                 &kCFTypeDictionaryValueCallBacks));
130
131   base::ScopedCFTypeRef<CFMutableDictionaryRef> traits_ref(
132       CFDictionaryCreateMutable(kCFAllocatorDefault,
133                                 0,
134                                 &kCFTypeDictionaryKeyCallBacks,
135                                 &kCFTypeDictionaryValueCallBacks));
136   if (!attributes_ref || !traits_ref)
137     return;
138
139   CFDictionaryAddValue(attributes_ref, kCTFontTraitsAttribute, traits_ref);
140
141   // Use symbolic traits to specify traits when possible.
142   CTFontSymbolicTraits symbolic_traits = 0;
143   if (desc.style & PP_TRUETYPEFONTSTYLE_ITALIC)
144     symbolic_traits |= kCTFontItalicTrait;
145   if (desc.weight == PP_TRUETYPEFONTWEIGHT_BOLD)
146     symbolic_traits |= kCTFontBoldTrait;
147   if (desc.width == PP_TRUETYPEFONTWIDTH_CONDENSED)
148     symbolic_traits |= kCTFontCondensedTrait;
149   else if (desc.width == PP_TRUETYPEFONTWIDTH_EXPANDED)
150     symbolic_traits |= kCTFontExpandedTrait;
151
152   base::ScopedCFTypeRef<CFNumberRef> symbolic_traits_ref(CFNumberCreate(
153       kCFAllocatorDefault, kCFNumberSInt32Type, &symbolic_traits));
154   if (!symbolic_traits_ref)
155     return;
156   CFDictionaryAddValue(traits_ref, kCTFontSymbolicTrait, symbolic_traits_ref);
157
158   // Font family matching doesn't work using family classes in symbolic traits.
159   // Instead, map generic_family to font families that are always available.
160   std::string family(desc.family);
161   if (family.empty()) {
162     switch (desc.generic_family) {
163       case PP_TRUETYPEFONTFAMILY_SERIF:
164         family = "Times";
165         break;
166       case PP_TRUETYPEFONTFAMILY_SANSSERIF:
167         family = "Helvetica";
168         break;
169       case PP_TRUETYPEFONTFAMILY_CURSIVE:
170         family = "Apple Chancery";
171         break;
172       case PP_TRUETYPEFONTFAMILY_FANTASY:
173         family = "Papyrus";
174         break;
175       case PP_TRUETYPEFONTFAMILY_MONOSPACE:
176         family = "Courier";
177         break;
178     }
179   }
180
181   base::ScopedCFTypeRef<CFStringRef> name_ref(
182       base::SysUTF8ToCFStringRef(family));
183   if (name_ref)
184     CFDictionaryAddValue(attributes_ref, kCTFontFamilyNameAttribute, name_ref);
185
186   if (desc.weight != PP_TRUETYPEFONTWEIGHT_NORMAL &&
187       desc.weight != PP_TRUETYPEFONTWEIGHT_BOLD) {
188     float weight = GetMacWeight(desc.weight);
189     base::ScopedCFTypeRef<CFNumberRef> weight_trait_ref(
190         CFNumberCreate(kCFAllocatorDefault, kCFNumberFloat32Type, &weight));
191     if (weight_trait_ref)
192       CFDictionaryAddValue(traits_ref, kCTFontWeightTrait, weight_trait_ref);
193   }
194
195   if (desc.width != PP_TRUETYPEFONTWIDTH_NORMAL &&
196       desc.width != PP_TRUETYPEFONTWIDTH_CONDENSED &&
197       desc.width != PP_TRUETYPEFONTWIDTH_EXPANDED) {
198     float width = GetMacWidth(desc.width);
199     base::ScopedCFTypeRef<CFNumberRef> width_trait_ref(
200         CFNumberCreate(kCFAllocatorDefault, kCFNumberFloat32Type, &width));
201     if (width_trait_ref)
202       CFDictionaryAddValue(traits_ref, kCTFontWidthTrait, width_trait_ref);
203   }
204
205   base::ScopedCFTypeRef<CTFontDescriptorRef> desc_ref(
206       CTFontDescriptorCreateWithAttributes(attributes_ref));
207
208   if (desc_ref)
209     font_ref_.reset(CTFontCreateWithFontDescriptor(desc_ref, 0, NULL));
210 }
211
212 PepperTrueTypeFontMac::~PepperTrueTypeFontMac() {
213 }
214
215 bool PepperTrueTypeFontMac::IsValid() {
216   return font_ref_.get() != NULL;
217 }
218
219 int32_t PepperTrueTypeFontMac::Describe(
220     ppapi::proxy::SerializedTrueTypeFontDesc* desc) {
221   if (!IsValid())
222     return PP_ERROR_FAILED;
223
224   base::ScopedCFTypeRef<CTFontDescriptorRef> desc_ref(
225       CTFontCopyFontDescriptor(font_ref_));
226
227   base::ScopedCFTypeRef<CFStringRef> family_name_ref(
228       base::mac::CFCast<CFStringRef>(
229           CTFontDescriptorCopyAttribute(desc_ref, kCTFontFamilyNameAttribute)));
230   desc->family = base::SysCFStringRefToUTF8(family_name_ref);
231
232   base::ScopedCFTypeRef<CFDictionaryRef> traits_ref(
233       base::mac::CFCast<CFDictionaryRef>(
234           CTFontDescriptorCopyAttribute(desc_ref, kCTFontTraitsAttribute)));
235
236   desc->style = PP_TRUETYPEFONTSTYLE_NORMAL;
237   CTFontSymbolicTraits symbolic_traits(CTFontGetSymbolicTraits(font_ref_));
238   if (symbolic_traits & kCTFontItalicTrait)
239     desc->style = static_cast<PP_TrueTypeFontStyle_Dev>(
240                       desc->style | PP_TRUETYPEFONTSTYLE_ITALIC);
241   if (symbolic_traits & kCTFontBoldTrait) {
242     desc->weight = PP_TRUETYPEFONTWEIGHT_BOLD;
243   } else {
244     float weight;
245     if (FindFloat(traits_ref, kCTFontWeightTrait, &weight))
246       desc->weight = GetPepperWeight(weight);
247   }
248   if (symbolic_traits & kCTFontCondensedTrait) {
249     desc->width = PP_TRUETYPEFONTWIDTH_CONDENSED;
250   } else if (symbolic_traits & kCTFontExpandedTrait) {
251     desc->width = PP_TRUETYPEFONTWIDTH_EXPANDED;
252   } else {
253     float width;
254     if (FindFloat(traits_ref, kCTFontWidthTrait, &width))
255       desc->width = GetPepperWidth(width);
256   }
257
258   // Character set isn't supported on Mac.
259   desc->charset = PP_TRUETYPEFONTCHARSET_DEFAULT;
260   return PP_OK;
261 }
262
263 int32_t PepperTrueTypeFontMac::GetTableTags(std::vector<uint32_t>* tags) {
264   base::ScopedCFTypeRef<CFArrayRef> tag_array(
265       CTFontCopyAvailableTables(font_ref_, kCTFontTableOptionNoOptions));
266   if (!tag_array)
267     return PP_ERROR_FAILED;
268
269   // Items returned by CTFontCopyAvailableTables are not boxed. Whose bright
270   // idea was this?
271   CFIndex length = CFArrayGetCount(tag_array);
272   tags->resize(length);
273   for (CFIndex i = 0; i < length; ++i) {
274     (*tags)[i] =
275         reinterpret_cast<uintptr_t>(CFArrayGetValueAtIndex(tag_array, i));
276   }
277   return length;
278 }
279
280 int32_t PepperTrueTypeFontMac::GetTable(uint32_t table_tag,
281                                         int32_t offset,
282                                         int32_t max_data_length,
283                                         std::string* data) {
284   if (!table_tag)
285     return GetEntireFont(offset, max_data_length, data);
286
287   base::ScopedCFTypeRef<CFDataRef> table_ref(
288       CTFontCopyTable(font_ref_,
289                       static_cast<CTFontTableTag>(table_tag),
290                       kCTFontTableOptionNoOptions));
291   if (!table_ref)
292     return PP_ERROR_FAILED;
293
294   CFIndex table_size = CFDataGetLength(table_ref);
295   CFIndex safe_offset =
296       std::min(base::checked_numeric_cast<CFIndex>(offset), table_size);
297   CFIndex safe_length =
298       std::min(table_size - safe_offset,
299                base::checked_numeric_cast<CFIndex>(max_data_length));
300   data->resize(safe_length);
301   CFDataGetBytes(table_ref, CFRangeMake(safe_offset, safe_length),
302                  reinterpret_cast<UInt8*>(&(*data)[0]));
303
304   return safe_length;
305 }
306
307 int32_t PepperTrueTypeFontMac::GetEntireFont(int32_t offset,
308                                              int32_t max_data_length,
309                                              std::string* data) {
310   // Reconstruct the font header, table directory, and tables.
311   std::vector<uint32_t> table_tags;
312   int32_t table_count = GetTableTags(&table_tags);
313   if (table_count < 0)
314     return table_count;  // PPAPI error code.
315
316   // Allocate enough room for the header and the table directory entries.
317   std::string font(sizeof(FontHeader) +
318                    sizeof(FontDirectoryEntry) * table_count, 0);
319   // Map the OS X font type value to a TrueType scalar type.
320   base::ScopedCFTypeRef<CFNumberRef> font_type_ref(
321       base::mac::CFCast<CFNumberRef>(
322           CTFontCopyAttribute(font_ref_, kCTFontFormatAttribute)));
323   int32_t font_type;
324   CFNumberGetValue(font_type_ref, kCFNumberSInt32Type, &font_type);
325   switch (font_type) {
326     case kCTFontFormatOpenTypePostScript:
327       font_type = MAKE_TABLE_TAG('O', 'T', 'T', 'O');
328       break;
329     case kCTFontFormatTrueType:
330     case kCTFontFormatBitmap:
331       font_type = MAKE_TABLE_TAG('t', 'r', 'u', 'e');
332       break;
333     case kCTFontFormatPostScript:
334       font_type = MAKE_TABLE_TAG('t', 'y', 'p', '1');
335       break;
336     case kCTFontFormatOpenTypeTrueType:
337     case kCTFontFormatUnrecognized:
338     default:
339       font_type = MAKE_TABLE_TAG(0, 1, 0, 0);
340       break;
341   }
342
343   // Calculate the rest of the header values.
344   uint16_t num_tables = base::checked_numeric_cast<uint16_t>(table_count);
345   uint16_t entry_selector = 0;
346   uint16_t search_range = 1;
347   while (search_range < num_tables >> 1) {
348     entry_selector++;
349     search_range <<= 1;
350   }
351   search_range <<= 4;
352   uint16_t range_shift = (num_tables << 4) - search_range;
353
354   // Write the header, with values in big-endian order.
355   FontHeader* font_header = reinterpret_cast<FontHeader*>(&font[0]);
356   font_header->font_type = base::HostToNet32(font_type);
357   font_header->num_tables = base::HostToNet16(num_tables);
358   font_header->search_range = base::HostToNet16(search_range);
359   font_header->entry_selector = base::HostToNet16(entry_selector);
360   font_header->range_shift = base::HostToNet16(range_shift);
361
362   for (int32_t i = 0; i < table_count; i++) {
363     // Get the table data.
364     std::string table;
365     int32_t table_size = GetTable(table_tags[i],
366                                   0, std::numeric_limits<int32_t>::max(),
367                                   &table);
368     if (table_size < 0)
369       return table_size;  // PPAPI error code.
370
371     // Append it to the font data so far, and zero pad so tables stay aligned.
372     size_t table_offset = font.size();
373     font.append(table);
374     size_t padding = font.size() & 0x3;
375     font.append(padding, 0);
376
377     // Fill in the directory entry for this table.
378     FontDirectoryEntry* entry = reinterpret_cast<FontDirectoryEntry*>(
379         &font[0] + sizeof(FontHeader) + i * sizeof(FontDirectoryEntry));
380     entry->tag = base::HostToNet32(table_tags[i]);
381     entry->checksum = base::HostToNet32(
382                           CalculateChecksum(&font[table_offset], table_size));
383     entry->offset = base::HostToNet32(table_offset);
384     entry->logical_length = base::HostToNet32(table_size);
385     // TODO(bbudge) set the 'head' table checksumAdjustment.
386   }
387
388   // Extract a substring if the caller specified an offset or max data length.
389   int32_t font_size = base::checked_numeric_cast<int32_t>(font.size());
390   int32_t safe_offset = std::min(offset, font_size);
391   int32_t safe_length = std::min(font_size - safe_offset, max_data_length);
392   if (safe_offset || safe_length != font_size)
393     font = font.substr(safe_offset, safe_length);
394
395   data->clear();
396   data->swap(font);
397   return safe_length;
398 }
399
400 }  // namespace
401
402 // static
403 PepperTrueTypeFont* PepperTrueTypeFont::Create(
404     const ppapi::proxy::SerializedTrueTypeFontDesc& desc) {
405   return new PepperTrueTypeFontMac(desc);
406 }
407
408 }  // namespace content