blob: 2087046ce079797df552d93846afe9e2049475f6 [file] [log] [blame]
// Copyright 2014 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 "content/renderer/renderer_font_platform_win.h"
#include <dwrite.h>
#include <string>
#include <vector>
#include <wrl/implements.h>
#include <wrl/wrappers/corewrappers.h>
#include "base/debug/alias.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/memory_mapped_file.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "base/path_service.h"
#include "base/time/time.h"
#include "base/win/iat_patch_function.h"
#include "base/win/registry.h"
#include "base/win/scoped_comptr.h"
namespace {
namespace mswr = Microsoft::WRL;
namespace mswrw = Microsoft::WRL::Wrappers;
class FontCollectionLoader
: public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>,
IDWriteFontCollectionLoader> {
public:
// IDWriteFontCollectionLoader methods.
virtual HRESULT STDMETHODCALLTYPE
CreateEnumeratorFromKey(IDWriteFactory* factory,
void const* key,
UINT32 key_size,
IDWriteFontFileEnumerator** file_enumerator);
static HRESULT Initialize(IDWriteFactory* factory);
UINT32 GetFontMapSize();
std::wstring GetFontNameFromKey(UINT32 idx);
bool LoadFontListFromRegistry();
FontCollectionLoader() {};
virtual ~FontCollectionLoader() {};
private:
mswr::ComPtr<IDWriteFontFileLoader> file_loader_;
std::vector<std::wstring> reg_fonts_;
};
mswr::ComPtr<FontCollectionLoader> g_font_loader;
class FontFileStream
: public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>,
IDWriteFontFileStream> {
public:
// IDWriteFontFileStream methods.
virtual HRESULT STDMETHODCALLTYPE
ReadFileFragment(void const** fragment_start,
UINT64 file_offset,
UINT64 fragment_size,
void** context) {
if (!memory_.get() || !memory_->IsValid())
return E_FAIL;
*fragment_start = static_cast<BYTE const*>(memory_->data()) +
static_cast<size_t>(file_offset);
*context = NULL;
return S_OK;
}
virtual void STDMETHODCALLTYPE ReleaseFileFragment(void* context) {}
virtual HRESULT STDMETHODCALLTYPE GetFileSize(UINT64* file_size) {
if (!memory_.get() || !memory_->IsValid())
return E_FAIL;
*file_size = memory_->length();
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE GetLastWriteTime(UINT64* last_write_time) {
if (!memory_.get() || !memory_->IsValid())
return E_FAIL;
// According to MSDN article http://goo.gl/rrSYzi the "last modified time"
// is used by DirectWrite font selection algorithms to determine whether
// one font resource is more up to date than another one.
// So by returning 0 we are assuming that it will treat all fonts to be
// equally up to date.
// TODO(shrikant): We should further investigate this.
*last_write_time = 0;
return S_OK;
}
FontFileStream::FontFileStream() : font_key_(0) {
};
HRESULT RuntimeClassInitialize(UINT32 font_key) {
base::FilePath path;
PathService::Get(base::DIR_WINDOWS_FONTS, &path);
path = path.Append(g_font_loader->GetFontNameFromKey(font_key).c_str());
memory_.reset(new base::MemoryMappedFile());
// Put some debug information on stack.
WCHAR font_name[256];
path.value().copy(font_name, arraysize(font_name));
base::debug::Alias(font_name);
if (!memory_->Initialize(path)) {
memory_.reset();
return E_FAIL;
}
font_key_ = font_key;
return S_OK;
}
virtual ~FontFileStream() {}
UINT32 font_key_;
scoped_ptr<base::MemoryMappedFile> memory_;
};
class FontFileLoader
: public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>,
IDWriteFontFileLoader> {
public:
// IDWriteFontFileLoader methods.
virtual HRESULT STDMETHODCALLTYPE
CreateStreamFromKey(void const* ref_key,
UINT32 ref_key_size,
IDWriteFontFileStream** stream) {
if (ref_key_size != sizeof(UINT32))
return E_FAIL;
UINT32 font_key = *static_cast<const UINT32*>(ref_key);
mswr::ComPtr<FontFileStream> font_stream;
HRESULT hr = mswr::MakeAndInitialize<FontFileStream>(&font_stream,
font_key);
if (SUCCEEDED(hr)) {
*stream = font_stream.Detach();
return S_OK;
}
return E_FAIL;
}
FontFileLoader() {}
virtual ~FontFileLoader() {}
};
class FontFileEnumerator
: public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>,
IDWriteFontFileEnumerator> {
public:
// IDWriteFontFileEnumerator methods.
virtual HRESULT STDMETHODCALLTYPE MoveNext(BOOL* has_current_file) {
*has_current_file = FALSE;
if (current_file_)
current_file_.ReleaseAndGetAddressOf();
if (font_idx_ < g_font_loader->GetFontMapSize()) {
HRESULT hr =
factory_->CreateCustomFontFileReference(&font_idx_,
sizeof(UINT32),
file_loader_.Get(),
current_file_.GetAddressOf());
DCHECK(SUCCEEDED(hr));
*has_current_file = TRUE;
font_idx_++;
}
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE
GetCurrentFontFile(IDWriteFontFile** font_file) {
if (!current_file_) {
*font_file = NULL;
return E_FAIL;
}
*font_file = current_file_.Detach();
return S_OK;
}
FontFileEnumerator(const void* keys,
UINT32 buffer_size,
IDWriteFactory* factory,
IDWriteFontFileLoader* file_loader)
: factory_(factory), file_loader_(file_loader), font_idx_(0) {}
virtual ~FontFileEnumerator() {}
mswr::ComPtr<IDWriteFactory> factory_;
mswr::ComPtr<IDWriteFontFile> current_file_;
mswr::ComPtr<IDWriteFontFileLoader> file_loader_;
UINT32 font_idx_;
};
// IDWriteFontCollectionLoader methods.
HRESULT STDMETHODCALLTYPE FontCollectionLoader::CreateEnumeratorFromKey(
IDWriteFactory* factory,
void const* key,
UINT32 key_size,
IDWriteFontFileEnumerator** file_enumerator) {
*file_enumerator = mswr::Make<FontFileEnumerator>(
key, key_size, factory, file_loader_.Get()).Detach();
return S_OK;
}
// static
HRESULT FontCollectionLoader::Initialize(IDWriteFactory* factory) {
DCHECK(g_font_loader == NULL);
g_font_loader = mswr::Make<FontCollectionLoader>();
if (!g_font_loader) {
DCHECK(FALSE);
return E_FAIL;
}
CHECK(g_font_loader->LoadFontListFromRegistry());
g_font_loader->file_loader_ = mswr::Make<FontFileLoader>().Detach();
factory->RegisterFontFileLoader(g_font_loader->file_loader_.Get());
factory->RegisterFontCollectionLoader(g_font_loader.Get());
return S_OK;
}
UINT32 FontCollectionLoader::GetFontMapSize() {
return reg_fonts_.size();
}
std::wstring FontCollectionLoader::GetFontNameFromKey(UINT32 idx) {
DCHECK(idx < reg_fonts_.size());
return reg_fonts_[idx];
}
bool FontCollectionLoader::LoadFontListFromRegistry() {
const wchar_t kFontsRegistry[] =
L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts";
CHECK(reg_fonts_.empty());
base::win::RegKey regkey;
if (regkey.Open(HKEY_LOCAL_MACHINE, kFontsRegistry, KEY_READ) !=
ERROR_SUCCESS) {
return false;
}
std::wstring name;
std::wstring value;
for (DWORD idx = 0; idx < regkey.GetValueCount(); idx++) {
if (regkey.GetValueNameAt(idx, &name) == ERROR_SUCCESS &&
regkey.ReadValue(name.c_str(), &value) == ERROR_SUCCESS) {
base::FilePath path(value.c_str());
// We need to check if file name is the only component that exists,
// we will ignore all other registry entries.
std::vector<base::FilePath::StringType> components;
path.GetComponents(&components);
if (components.size() == 1) {
reg_fonts_.push_back(value.c_str());
}
}
}
return true;
}
} // namespace
namespace content {
mswr::ComPtr<IDWriteFontCollection> g_font_collection;
IDWriteFontCollection* GetCustomFontCollection(IDWriteFactory* factory) {
if (g_font_collection.Get() != NULL)
return g_font_collection.Get();
base::TimeTicks start_tick = base::TimeTicks::Now();
FontCollectionLoader::Initialize(factory);
HRESULT hr = factory->CreateCustomFontCollection(
g_font_loader.Get(), NULL, 0, g_font_collection.GetAddressOf());
base::TimeDelta time_delta = base::TimeTicks::Now() - start_tick;
int64 delta = time_delta.ToInternalValue();
base::debug::Alias(&delta);
UINT32 size = g_font_loader->GetFontMapSize();
base::debug::Alias(&size);
CHECK(SUCCEEDED(hr));
CHECK(g_font_collection.Get() != NULL);
return g_font_collection.Get();
}
} // namespace content