summaryrefslogtreecommitdiffstats
path: root/chrome/common/font_loader_mac.mm
blob: a6b9c2503cfcd6cc176feb6f0085dd3d57ff605a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/common/font_loader_mac.h"

#import <Cocoa/Cocoa.h>

#include "base/basictypes.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/mac_util.h"
#include "base/scoped_cftyperef.h"
#include "base/sys_string_conversions.h"

// static
bool FontLoader::LoadFontIntoBuffer(const string16& font_name,
                                    float font_point_size,
                                    base::SharedMemory* font_data,
                                    uint32* font_data_size) {
  CHECK(font_data && font_data_size);
  *font_data_size = 0;

  // Load appropriate NSFont.
  NSString* font_name_ns = base::SysUTF16ToNSString(font_name);
  NSFont* font_to_encode =
      [NSFont fontWithName:font_name_ns size:font_point_size];
  if (!font_to_encode) {
    LOG(ERROR) << "Failed to load font " << font_name;
    return false;
  }

  // NSFont -> ATSFontRef.
  ATSFontRef ats_font =
      CTFontGetPlatformFont(reinterpret_cast<CTFontRef>(font_to_encode), NULL);
  if (!ats_font) {
    LOG(ERROR) << "Conversion to ATSFontRef failed for " << font_name;
    return false;
  }

  // ATSFontRef -> File path.
  // Warning: Calling this function on a font activated from memory will result
  // in failure with a -50 - paramErr.  This may occur if
  // CreateCGFontFromBuffer() is called in the same process as this function
  // e.g. when writing a unit test that exercises these two functions together.
  // If said unit test were to load a system font and activate it from memory
  // it becomes impossible for the system to the find the original file ref
  // since the font now lives in memory as far as it's concerned.
  FSRef font_fsref;
  if (ATSFontGetFileReference(ats_font, &font_fsref) != noErr) {
    LOG(ERROR) << "Failed to find font file for " << font_name;
    return false;
  }
  FilePath font_path = FilePath(mac_util::PathFromFSRef(font_fsref));

  // Load file into shared memory buffer.
  int64 font_file_size_64 = -1;
  if (!file_util::GetFileSize(font_path, &font_file_size_64)) {
    LOG(ERROR) << "Couldn't get font file size for " << font_path.value();
    return false;
  }

  if (font_file_size_64 <= 0 || font_file_size_64 >= kint32max) {
    LOG(ERROR) << "Bad size for font file " << font_path.value();
    return false;
  }

  int32 font_file_size_32 = static_cast<int32>(font_file_size_64);
  if (!font_data->Create(L"", false, false, font_file_size_32)) {
    LOG(ERROR) << "Failed to create shmem area for " << font_name;
    return false;
  }

  if (!font_data->Map(font_file_size_32)) {
    LOG(ERROR) << "Failed to map shmem area for " << font_name;
    return false;
  }

  int32 amt_read = file_util::ReadFile(font_path,
                       reinterpret_cast<char*>(font_data->memory()),
                       font_file_size_32);
  if (amt_read != font_file_size_32) {
    LOG(ERROR) << "Failed to read font data for " << font_path.value();
    return false;
  }

  *font_data_size = font_file_size_32;
  return true;
}

// static
bool FontLoader::CreateCGFontFromBuffer(base::SharedMemoryHandle font_data,
                                        uint32 font_data_size,
                                        CGFontRef *font) {
  using base::SharedMemory;
  DCHECK(SharedMemory::IsHandleValid(font_data));
  DCHECK_GT(font_data_size, 0U);

  SharedMemory shm(font_data, true);
  if (!shm.Map(font_data_size))
    return false;

  ATSFontContainerRef font_container = 0;
  OSStatus err = ATSFontActivateFromMemory(shm.memory(), font_data_size,
                    kATSFontContextLocal, kATSFontFormatUnspecified,
                    NULL, kATSOptionFlagsDefault, &font_container );
  if (err != noErr || !font_container)
    return false;

  // Count the number of fonts that were loaded.
  ItemCount fontCount = 0;
  err = ATSFontFindFromContainer(font_container, kATSOptionFlagsDefault, 0,
             NULL, &fontCount);

  if (err != noErr || fontCount < 1) {
    ATSFontDeactivate(font_container, NULL, kATSOptionFlagsDefault);
    return false;
  }

  // Load font from container.
  ATSFontRef font_ref_ats = 0;
  ATSFontFindFromContainer(font_container, kATSOptionFlagsDefault, 1,
      &font_ref_ats, NULL);

  if (!font_ref_ats) {
    ATSFontDeactivate(font_container, NULL, kATSOptionFlagsDefault);
    return false;
  }

  // Convert to cgFont.
  CGFontRef font_ref_cg = CGFontCreateWithPlatformFont(&font_ref_ats);

  if (!font_ref_cg) {
    ATSFontDeactivate(font_container, NULL, kATSOptionFlagsDefault);
    return false;
  }

  *font = font_ref_cg;
  return true;
}