| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/child/dwrite_font_proxy/font_fallback_win.h" | 5 #include "content/child/dwrite_font_proxy/font_fallback_win.h" |
| 6 | 6 |
| 7 #include <math.h> |
| 8 |
| 9 #include <algorithm> |
| 10 |
| 11 #include "base/metrics/histogram_macros.h" |
| 7 #include "base/strings/string16.h" | 12 #include "base/strings/string16.h" |
| 8 | 13 #include "base/strings/utf_string_conversion_utils.h" |
| 9 #include "content/child/dwrite_font_proxy/dwrite_font_proxy_win.h" | 14 #include "content/child/dwrite_font_proxy/dwrite_font_proxy_win.h" |
| 10 #include "content/common/dwrite_font_proxy_messages.h" | 15 #include "content/common/dwrite_font_proxy_messages.h" |
| 11 #include "content/public/child/child_thread.h" | 16 #include "content/public/child/child_thread.h" |
| 12 #include "ipc/ipc_sender.h" | 17 #include "ipc/ipc_sender.h" |
| 13 | 18 |
| 14 namespace mswr = Microsoft::WRL; | 19 namespace mswr = Microsoft::WRL; |
| 15 | 20 |
| 16 namespace content { | 21 namespace content { |
| 17 | 22 |
| 23 namespace { |
| 24 |
| 25 const size_t kMaxFamilyCacheSize = 10; |
| 26 |
| 27 // This enum is used to define the buckets for an enumerated UMA histogram. |
| 28 // Hence, |
| 29 // (a) existing enumerated constants should never be deleted or reordered, and |
| 30 // (b) new constants should only be appended at the end of the enumeration. |
| 31 enum DirectWriteFontFallbackResult { |
| 32 FAILED_NO_FONT = 0, |
| 33 SUCCESS_CACHE = 1, |
| 34 SUCCESS_IPC = 2, |
| 35 |
| 36 FONT_FALLBACK_RESULT_MAX_VALUE |
| 37 }; |
| 38 |
| 39 void LogFallbackResult(DirectWriteFontFallbackResult fallback_result) { |
| 40 UMA_HISTOGRAM_ENUMERATION("DirectWrite.Fonts.Proxy.FallbackResult", |
| 41 fallback_result, FONT_FALLBACK_RESULT_MAX_VALUE); |
| 42 } |
| 43 |
| 44 } // namespace |
| 45 |
| 18 FontFallback::FontFallback() = default; | 46 FontFallback::FontFallback() = default; |
| 19 FontFallback::~FontFallback() = default; | 47 FontFallback::~FontFallback() = default; |
| 20 | 48 |
| 21 HRESULT FontFallback::MapCharacters(IDWriteTextAnalysisSource* source, | 49 HRESULT FontFallback::MapCharacters(IDWriteTextAnalysisSource* source, |
| 22 UINT32 text_position, | 50 UINT32 text_position, |
| 23 UINT32 text_length, | 51 UINT32 text_length, |
| 24 IDWriteFontCollection* base_font_collection, | 52 IDWriteFontCollection* base_font_collection, |
| 25 const wchar_t* base_family_name, | 53 const wchar_t* base_family_name, |
| 26 DWRITE_FONT_WEIGHT base_weight, | 54 DWRITE_FONT_WEIGHT base_weight, |
| 27 DWRITE_FONT_STYLE base_style, | 55 DWRITE_FONT_STYLE base_style, |
| 28 DWRITE_FONT_STRETCH base_stretch, | 56 DWRITE_FONT_STRETCH base_stretch, |
| 29 UINT32* mapped_length, | 57 UINT32* mapped_length, |
| 30 IDWriteFont** mapped_font, | 58 IDWriteFont** mapped_font, |
| 31 FLOAT* scale) { | 59 FLOAT* scale) { |
| 32 *mapped_font = nullptr; | 60 *mapped_font = nullptr; |
| 33 *mapped_length = 1; | 61 *mapped_length = 1; |
| 34 *scale = 1.0; | 62 *scale = 1.0; |
| 35 | 63 |
| 36 const WCHAR* text = nullptr; | 64 const WCHAR* text = nullptr; |
| 37 UINT32 chunk_length = 0; | 65 UINT32 chunk_length = 0; |
| 38 if (FAILED(source->GetTextAtPosition(text_position, &text, &chunk_length))) { | 66 if (FAILED(source->GetTextAtPosition(text_position, &text, &chunk_length))) { |
| 39 DCHECK(false); | 67 DCHECK(false); |
| 40 return E_FAIL; | 68 return E_FAIL; |
| 41 } | 69 } |
| 42 base::string16 text_chunk(text, chunk_length); | 70 base::string16 text_chunk(text, std::min(chunk_length, text_length)); |
| 71 |
| 72 if (text_chunk.size() == 0) { |
| 73 DCHECK(false); |
| 74 return E_INVALIDARG; |
| 75 } |
| 76 |
| 77 base_family_name = base_family_name ? base_family_name : L""; |
| 78 |
| 79 if (GetCachedFont(text_chunk, base_family_name, base_weight, base_style, |
| 80 base_stretch, mapped_font, mapped_length)) { |
| 81 DCHECK(*mapped_font); |
| 82 DCHECK_GT(*mapped_length, 0u); |
| 83 LogFallbackResult(SUCCESS_CACHE); |
| 84 return S_OK; |
| 85 } |
| 86 |
| 87 TRACE_EVENT0("dwrite", "FontFallback::MapCharacters (IPC)"); |
| 43 | 88 |
| 44 const WCHAR* locale = nullptr; | 89 const WCHAR* locale = nullptr; |
| 45 // |locale_text_length| is actually the length of text with the locale, not | 90 // |locale_text_length| is actually the length of text with the locale, not |
| 46 // the length of the locale string itself. | 91 // the length of the locale string itself. |
| 47 UINT32 locale_text_length = 0; | 92 UINT32 locale_text_length = 0; |
| 48 source->GetLocaleName(text_position /*textPosition*/, &locale_text_length, | 93 source->GetLocaleName(text_position /*textPosition*/, &locale_text_length, |
| 49 &locale); | 94 &locale); |
| 50 | 95 |
| 51 if (locale == nullptr) | 96 locale = locale ? locale : L""; |
| 52 locale = L""; | |
| 53 | 97 |
| 54 DWriteFontStyle style; | 98 DWriteFontStyle style; |
| 55 style.font_weight = base_weight; | 99 style.font_weight = base_weight; |
| 56 style.font_slant = base_style; | 100 style.font_slant = base_style; |
| 57 style.font_stretch = base_stretch; | 101 style.font_stretch = base_stretch; |
| 58 | 102 |
| 59 MapCharactersResult result; | 103 MapCharactersResult result; |
| 60 | 104 |
| 61 IPC::Sender* sender = | 105 IPC::Sender* sender = |
| 62 sender_override_ ? sender_override_ : ChildThread::Get(); | 106 sender_override_ ? sender_override_ : ChildThread::Get(); |
| 63 if (!sender->Send(new DWriteFontProxyMsg_MapCharacters( | 107 if (!sender->Send(new DWriteFontProxyMsg_MapCharacters( |
| 64 text_chunk, style, locale, source->GetParagraphReadingDirection(), | 108 text_chunk, style, locale, source->GetParagraphReadingDirection(), |
| 65 base_family_name ? base_family_name : L"", &result))) | 109 base_family_name, &result))) { |
| 110 DCHECK(false); |
| 66 return E_FAIL; | 111 return E_FAIL; |
| 112 } |
| 113 |
| 114 // We don't cache scale in the fallback cache, and Skia ignores scale anyway. |
| 115 // If we ever get a result that's significantly different from 1 we may need |
| 116 // to consider whether it's worth doing the work to plumb it through. |
| 117 DCHECK(fabs(*scale - 1.0f) < 0.00001); |
| 67 | 118 |
| 68 *mapped_length = result.mapped_length; | 119 *mapped_length = result.mapped_length; |
| 69 *scale = result.scale; | 120 *scale = result.scale; |
| 70 | 121 |
| 71 if (result.family_index == UINT32_MAX) | 122 if (result.family_index == UINT32_MAX) { |
| 123 LogFallbackResult(FAILED_NO_FONT); |
| 72 return S_OK; | 124 return S_OK; |
| 125 } |
| 73 | 126 |
| 74 mswr::ComPtr<IDWriteFontFamily> family; | 127 mswr::ComPtr<IDWriteFontFamily> family; |
| 75 // It would be nice to find a way to determine at runtime if |collection_| is | 128 // It would be nice to find a way to determine at runtime if |collection_| is |
| 76 // a proxy collection, or just a generic IDWriteFontCollection. Unfortunately | 129 // a proxy collection, or just a generic IDWriteFontCollection. Unfortunately |
| 77 // I can't find a way to get QueryInterface to return the actual class when | 130 // I can't find a way to get QueryInterface to return the actual class when |
| 78 // using mswr::RuntimeClass. If we could use QI, we can fallback on | 131 // using mswr::RuntimeClass. If we could use QI, we can fallback on |
| 79 // FindFontFamily if the proxy is not available. | 132 // FindFontFamily if the proxy is not available. |
| 80 if (!collection_->GetFontFamily(result.family_index, result.family_name, | 133 if (!collection_->GetFontFamily(result.family_index, result.family_name, |
| 81 &family)) { | 134 &family)) { |
| 82 DCHECK(false); | 135 DCHECK(false); |
| 83 return E_FAIL; | 136 return E_FAIL; |
| 84 } | 137 } |
| 85 | 138 |
| 86 if (FAILED(family->GetFirstMatchingFont( | 139 if (FAILED(family->GetFirstMatchingFont( |
| 87 static_cast<DWRITE_FONT_WEIGHT>(result.font_style.font_weight), | 140 static_cast<DWRITE_FONT_WEIGHT>(result.font_style.font_weight), |
| 88 static_cast<DWRITE_FONT_STRETCH>(result.font_style.font_stretch), | 141 static_cast<DWRITE_FONT_STRETCH>(result.font_style.font_stretch), |
| 89 static_cast<DWRITE_FONT_STYLE>(result.font_style.font_slant), | 142 static_cast<DWRITE_FONT_STYLE>(result.font_style.font_slant), |
| 90 mapped_font))) { | 143 mapped_font))) { |
| 91 DCHECK(false); | 144 DCHECK(false); |
| 92 return E_FAIL; | 145 return E_FAIL; |
| 93 } | 146 } |
| 94 | 147 |
| 95 DCHECK(*mapped_font); | 148 DCHECK(*mapped_font); |
| 96 | 149 AddCachedFamily(std::move(family), base_family_name); |
| 150 LogFallbackResult(SUCCESS_IPC); |
| 97 return S_OK; | 151 return S_OK; |
| 98 } | 152 } |
| 99 | 153 |
| 100 HRESULT STDMETHODCALLTYPE | 154 HRESULT STDMETHODCALLTYPE |
| 101 FontFallback::RuntimeClassInitialize(DWriteFontCollectionProxy* collection, | 155 FontFallback::RuntimeClassInitialize(DWriteFontCollectionProxy* collection, |
| 102 IPC::Sender* sender_override) { | 156 IPC::Sender* sender_override) { |
| 103 sender_override_ = sender_override; | 157 sender_override_ = sender_override; |
| 104 collection_ = collection; | 158 collection_ = collection; |
| 105 return S_OK; | 159 return S_OK; |
| 106 } | 160 } |
| 107 | 161 |
| 162 bool FontFallback::GetCachedFont(const base::string16& text, |
| 163 const wchar_t* base_family_name, |
| 164 DWRITE_FONT_WEIGHT base_weight, |
| 165 DWRITE_FONT_STYLE base_style, |
| 166 DWRITE_FONT_STRETCH base_stretch, |
| 167 IDWriteFont** font, |
| 168 uint32_t* mapped_length) { |
| 169 std::map<base::string16, std::list<mswr::ComPtr<IDWriteFontFamily>>>::iterator |
| 170 it = fallback_family_cache_.find(base_family_name); |
| 171 if (it == fallback_family_cache_.end()) |
| 172 return false; |
| 173 |
| 174 TRACE_EVENT0("dwrite", "FontFallback::GetCachedFont"); |
| 175 |
| 176 std::list<mswr::ComPtr<IDWriteFontFamily>>& family_list = it->second; |
| 177 std::list<mswr::ComPtr<IDWriteFontFamily>>::iterator family_iterator; |
| 178 for (family_iterator = family_list.begin(); |
| 179 family_iterator != family_list.end(); ++family_iterator) { |
| 180 mswr::ComPtr<IDWriteFont> matched_font; |
| 181 (*family_iterator)->GetFirstMatchingFont(base_weight, base_stretch, |
| 182 base_style, &matched_font); |
| 183 |
| 184 // |character_index| tracks how much of the string we have read. This is |
| 185 // different from |mapped_length| because ReadUnicodeCharacter can advance |
| 186 // |character_index| even if the character cannot be mapped (invalid |
| 187 // surrogate pair or font does not contain a matching glyph). |
| 188 int32_t character_index = 0; |
| 189 uint32_t length = 0; // How much of the text can actually be mapped. |
| 190 while (static_cast<uint32_t>(character_index) < text.length()) { |
| 191 BOOL exists = false; |
| 192 uint32_t character = 0; |
| 193 if (!base::ReadUnicodeCharacter(text.c_str(), text.length(), |
| 194 &character_index, &character)) |
| 195 break; |
| 196 if (FAILED(matched_font->HasCharacter(character, &exists)) || !exists) |
| 197 break; |
| 198 character_index++; |
| 199 length = character_index; |
| 200 } |
| 201 |
| 202 if (length > 0) { |
| 203 // Move the current family to the front of the list |
| 204 family_list.splice(family_list.begin(), family_list, family_iterator); |
| 205 |
| 206 matched_font.CopyTo(font); |
| 207 *mapped_length = length; |
| 208 return true; |
| 209 } |
| 210 } |
| 211 |
| 212 return false; |
| 213 } |
| 214 |
| 215 void FontFallback::AddCachedFamily( |
| 216 Microsoft::WRL::ComPtr<IDWriteFontFamily> family, |
| 217 const wchar_t* base_family_name) { |
| 218 std::list<mswr::ComPtr<IDWriteFontFamily>>& family_list = |
| 219 fallback_family_cache_[base_family_name]; |
| 220 family_list.push_front(std::move(family)); |
| 221 |
| 222 UMA_HISTOGRAM_COUNTS_100("DirectWrite.Fonts.Proxy.Fallback.CacheSize", |
| 223 family_list.size()); |
| 224 |
| 225 while (family_list.size() > kMaxFamilyCacheSize) |
| 226 family_list.pop_back(); |
| 227 } |
| 228 |
| 108 } // namespace content | 229 } // namespace content |
| OLD | NEW |