blob: 4bf78d2804f04dac61795a100917cf8ed740fe6d [file] [log] [blame]
// Copyright 2016 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/child/dwrite_font_proxy/font_fallback_win.h"
#include <math.h>
#include <algorithm>
#include "base/metrics/histogram_macros.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversion_utils.h"
#include "content/child/dwrite_font_proxy/dwrite_font_proxy_win.h"
#include "content/common/dwrite_font_proxy_messages.h"
#include "content/public/child/child_thread.h"
#include "ipc/ipc_sender.h"
namespace mswr = Microsoft::WRL;
namespace content {
namespace {
const size_t kMaxFamilyCacheSize = 10;
// This enum is used to define the buckets for an enumerated UMA histogram.
// Hence,
// (a) existing enumerated constants should never be deleted or reordered, and
// (b) new constants should only be appended at the end of the enumeration.
enum DirectWriteFontFallbackResult {
FAILED_NO_FONT = 0,
SUCCESS_CACHE = 1,
SUCCESS_IPC = 2,
FONT_FALLBACK_RESULT_MAX_VALUE
};
void LogFallbackResult(DirectWriteFontFallbackResult fallback_result) {
UMA_HISTOGRAM_ENUMERATION("DirectWrite.Fonts.Proxy.FallbackResult",
fallback_result, FONT_FALLBACK_RESULT_MAX_VALUE);
}
} // namespace
FontFallback::FontFallback() = default;
FontFallback::~FontFallback() = default;
HRESULT FontFallback::MapCharacters(IDWriteTextAnalysisSource* source,
UINT32 text_position,
UINT32 text_length,
IDWriteFontCollection* base_font_collection,
const wchar_t* base_family_name,
DWRITE_FONT_WEIGHT base_weight,
DWRITE_FONT_STYLE base_style,
DWRITE_FONT_STRETCH base_stretch,
UINT32* mapped_length,
IDWriteFont** mapped_font,
FLOAT* scale) {
*mapped_font = nullptr;
*mapped_length = 1;
*scale = 1.0;
const WCHAR* text = nullptr;
UINT32 chunk_length = 0;
if (FAILED(source->GetTextAtPosition(text_position, &text, &chunk_length))) {
DCHECK(false);
return E_FAIL;
}
base::string16 text_chunk(text, std::min(chunk_length, text_length));
if (text_chunk.size() == 0) {
DCHECK(false);
return E_INVALIDARG;
}
base_family_name = base_family_name ? base_family_name : L"";
if (GetCachedFont(text_chunk, base_family_name, base_weight, base_style,
base_stretch, mapped_font, mapped_length)) {
DCHECK(*mapped_font);
DCHECK_GT(*mapped_length, 0u);
LogFallbackResult(SUCCESS_CACHE);
return S_OK;
}
TRACE_EVENT0("dwrite", "FontFallback::MapCharacters (IPC)");
const WCHAR* locale = nullptr;
// |locale_text_length| is actually the length of text with the locale, not
// the length of the locale string itself.
UINT32 locale_text_length = 0;
source->GetLocaleName(text_position /*textPosition*/, &locale_text_length,
&locale);
locale = locale ? locale : L"";
DWriteFontStyle style;
style.font_weight = base_weight;
style.font_slant = base_style;
style.font_stretch = base_stretch;
MapCharactersResult result;
IPC::Sender* sender =
sender_override_ ? sender_override_ : ChildThread::Get();
if (!sender->Send(new DWriteFontProxyMsg_MapCharacters(
text_chunk, style, locale, source->GetParagraphReadingDirection(),
base_family_name, &result))) {
DCHECK(false);
return E_FAIL;
}
// We don't cache scale in the fallback cache, and Skia ignores scale anyway.
// If we ever get a result that's significantly different from 1 we may need
// to consider whether it's worth doing the work to plumb it through.
DCHECK(fabs(*scale - 1.0f) < 0.00001);
*mapped_length = result.mapped_length;
*scale = result.scale;
if (result.family_index == UINT32_MAX) {
LogFallbackResult(FAILED_NO_FONT);
return S_OK;
}
mswr::ComPtr<IDWriteFontFamily> family;
// It would be nice to find a way to determine at runtime if |collection_| is
// a proxy collection, or just a generic IDWriteFontCollection. Unfortunately
// I can't find a way to get QueryInterface to return the actual class when
// using mswr::RuntimeClass. If we could use QI, we can fallback on
// FindFontFamily if the proxy is not available.
if (!collection_->GetFontFamily(result.family_index, result.family_name,
&family)) {
DCHECK(false);
return E_FAIL;
}
if (FAILED(family->GetFirstMatchingFont(
static_cast<DWRITE_FONT_WEIGHT>(result.font_style.font_weight),
static_cast<DWRITE_FONT_STRETCH>(result.font_style.font_stretch),
static_cast<DWRITE_FONT_STYLE>(result.font_style.font_slant),
mapped_font))) {
DCHECK(false);
return E_FAIL;
}
DCHECK(*mapped_font);
AddCachedFamily(std::move(family), base_family_name);
LogFallbackResult(SUCCESS_IPC);
return S_OK;
}
HRESULT STDMETHODCALLTYPE
FontFallback::RuntimeClassInitialize(DWriteFontCollectionProxy* collection,
IPC::Sender* sender_override) {
sender_override_ = sender_override;
collection_ = collection;
return S_OK;
}
bool FontFallback::GetCachedFont(const base::string16& text,
const wchar_t* base_family_name,
DWRITE_FONT_WEIGHT base_weight,
DWRITE_FONT_STYLE base_style,
DWRITE_FONT_STRETCH base_stretch,
IDWriteFont** font,
uint32_t* mapped_length) {
std::map<base::string16, std::list<mswr::ComPtr<IDWriteFontFamily>>>::iterator
it = fallback_family_cache_.find(base_family_name);
if (it == fallback_family_cache_.end())
return false;
TRACE_EVENT0("dwrite", "FontFallback::GetCachedFont");
std::list<mswr::ComPtr<IDWriteFontFamily>>& family_list = it->second;
std::list<mswr::ComPtr<IDWriteFontFamily>>::iterator family_iterator;
for (family_iterator = family_list.begin();
family_iterator != family_list.end(); ++family_iterator) {
mswr::ComPtr<IDWriteFont> matched_font;
(*family_iterator)->GetFirstMatchingFont(base_weight, base_stretch,
base_style, &matched_font);
// |character_index| tracks how much of the string we have read. This is
// different from |mapped_length| because ReadUnicodeCharacter can advance
// |character_index| even if the character cannot be mapped (invalid
// surrogate pair or font does not contain a matching glyph).
int32_t character_index = 0;
uint32_t length = 0; // How much of the text can actually be mapped.
while (static_cast<uint32_t>(character_index) < text.length()) {
BOOL exists = false;
uint32_t character = 0;
if (!base::ReadUnicodeCharacter(text.c_str(), text.length(),
&character_index, &character))
break;
if (FAILED(matched_font->HasCharacter(character, &exists)) || !exists)
break;
character_index++;
length = character_index;
}
if (length > 0) {
// Move the current family to the front of the list
family_list.splice(family_list.begin(), family_list, family_iterator);
matched_font.CopyTo(font);
*mapped_length = length;
return true;
}
}
return false;
}
void FontFallback::AddCachedFamily(
Microsoft::WRL::ComPtr<IDWriteFontFamily> family,
const wchar_t* base_family_name) {
std::list<mswr::ComPtr<IDWriteFontFamily>>& family_list =
fallback_family_cache_[base_family_name];
family_list.push_front(std::move(family));
UMA_HISTOGRAM_COUNTS_100("DirectWrite.Fonts.Proxy.Fallback.CacheSize",
family_list.size());
while (family_list.size() > kMaxFamilyCacheSize)
family_list.pop_back();
}
} // namespace content