OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "ui/gfx/render_text_win.h" | 5 #include "ui/gfx/render_text_win.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/i18n/break_iterator.h" | 9 #include "base/i18n/break_iterator.h" |
10 #include "base/i18n/char_iterator.h" | 10 #include "base/i18n/char_iterator.h" |
(...skipping 208 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
219 while (iter.Advance() && iter.array_pos() < run_length) { | 219 while (iter.Advance() && iter.array_pos() < run_length) { |
220 const UBlockCode current_block_code = ublock_getCode(iter.get()); | 220 const UBlockCode current_block_code = ublock_getCode(iter.get()); |
221 if (current_block_code != first_block_code && | 221 if (current_block_code != first_block_code && |
222 (first_block_unusual || IsUnusualBlockCode(current_block_code))) { | 222 (first_block_unusual || IsUnusualBlockCode(current_block_code))) { |
223 return run_start + iter.array_pos(); | 223 return run_start + iter.array_pos(); |
224 } | 224 } |
225 } | 225 } |
226 return run_break; | 226 return run_break; |
227 } | 227 } |
228 | 228 |
| 229 // Callback to |EnumEnhMetaFile()| to intercept font creation. |
| 230 int CALLBACK MetaFileEnumProc(HDC hdc, |
| 231 HANDLETABLE* table, |
| 232 CONST ENHMETARECORD* record, |
| 233 int table_entries, |
| 234 LPARAM log_font) { |
| 235 if (record->iType == EMR_EXTCREATEFONTINDIRECTW) { |
| 236 const EMREXTCREATEFONTINDIRECTW* create_font_record = |
| 237 reinterpret_cast<const EMREXTCREATEFONTINDIRECTW*>(record); |
| 238 *reinterpret_cast<LOGFONT*>(log_font) = create_font_record->elfw.elfLogFont; |
| 239 } |
| 240 return 1; |
| 241 } |
| 242 |
| 243 // Finds a fallback font to use to render the specified |text| with respect to |
| 244 // an initial |font|. Returns the resulting font via out param |result|. Returns |
| 245 // |true| if a fallback font was found. |
| 246 // Adapted from WebKit's |FontCache::GetFontDataForCharacters()|. |
| 247 // TODO(asvitkine): This should be moved to font_fallback_win.cc. |
| 248 bool ChooseFallbackFont(HDC hdc, |
| 249 const Font& font, |
| 250 const wchar_t* text, |
| 251 int text_length, |
| 252 Font* result) { |
| 253 // Use a meta file to intercept the fallback font chosen by Uniscribe. |
| 254 HDC meta_file_dc = CreateEnhMetaFile(hdc, NULL, NULL, NULL); |
| 255 if (!meta_file_dc) |
| 256 return false; |
| 257 |
| 258 SelectObject(meta_file_dc, font.GetNativeFont()); |
| 259 |
| 260 SCRIPT_STRING_ANALYSIS script_analysis; |
| 261 HRESULT hresult = |
| 262 ScriptStringAnalyse(meta_file_dc, text, text_length, 0, -1, |
| 263 SSA_METAFILE | SSA_FALLBACK | SSA_GLYPHS | SSA_LINK, |
| 264 0, NULL, NULL, NULL, NULL, NULL, &script_analysis); |
| 265 |
| 266 if (SUCCEEDED(hresult)) { |
| 267 hresult = ScriptStringOut(script_analysis, 0, 0, 0, NULL, 0, 0, FALSE); |
| 268 ScriptStringFree(&script_analysis); |
| 269 } |
| 270 |
| 271 bool found_fallback = false; |
| 272 HENHMETAFILE meta_file = CloseEnhMetaFile(meta_file_dc); |
| 273 if (SUCCEEDED(hresult)) { |
| 274 LOGFONT log_font; |
| 275 log_font.lfFaceName[0] = 0; |
| 276 EnumEnhMetaFile(0, meta_file, MetaFileEnumProc, &log_font, NULL); |
| 277 if (log_font.lfFaceName[0]) { |
| 278 *result = Font(base::UTF16ToUTF8(log_font.lfFaceName), |
| 279 font.GetFontSize()); |
| 280 found_fallback = true; |
| 281 } |
| 282 } |
| 283 DeleteEnhMetaFile(meta_file); |
| 284 |
| 285 return found_fallback; |
| 286 } |
| 287 |
229 } // namespace | 288 } // namespace |
230 | 289 |
231 namespace internal { | 290 namespace internal { |
232 | 291 |
233 TextRun::TextRun() | 292 TextRun::TextRun() |
234 : font_style(0), | 293 : font_style(0), |
235 strike(false), | 294 strike(false), |
236 diagonal_strike(false), | 295 diagonal_strike(false), |
237 underline(false), | 296 underline(false), |
238 width(0), | 297 width(0), |
(...skipping 753 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
992 run->width = abc.abcA + abc.abcB + abc.abcC; | 1051 run->width = abc.abcA + abc.abcB + abc.abcC; |
993 preceding_run_widths += run->width; | 1052 preceding_run_widths += run->width; |
994 } | 1053 } |
995 string_width_ = preceding_run_widths; | 1054 string_width_ = preceding_run_widths; |
996 } | 1055 } |
997 | 1056 |
998 void RenderTextWin::LayoutTextRun(internal::TextRun* run) { | 1057 void RenderTextWin::LayoutTextRun(internal::TextRun* run) { |
999 const size_t run_length = run->range.length(); | 1058 const size_t run_length = run->range.length(); |
1000 const wchar_t* run_text = &(GetLayoutText()[run->range.start()]); | 1059 const wchar_t* run_text = &(GetLayoutText()[run->range.start()]); |
1001 Font original_font = run->font; | 1060 Font original_font = run->font; |
1002 | 1061 internal::LinkedFontsIterator fonts(original_font); |
1003 run->logical_clusters.reset(new WORD[run_length]); | 1062 bool tried_cached_font = false; |
1004 | 1063 bool tried_fallback = false; |
1005 // Try shaping with |original_font|. | |
1006 Font current_font = original_font; | |
1007 int missing_count = CountCharsWithMissingGlyphs(run, | |
1008 ShapeTextRunWithFont(run, current_font)); | |
1009 if (missing_count == 0) | |
1010 return; | |
1011 | |
1012 // Keep track of the font that is able to display the greatest number of | 1064 // Keep track of the font that is able to display the greatest number of |
1013 // characters for which ScriptShape() returned S_OK. This font will be used | 1065 // characters for which ScriptShape() returned S_OK. This font will be used |
1014 // in the case where no font is able to display the entire run. | 1066 // in the case where no font is able to display the entire run. |
1015 int best_partial_font_missing_char_count = missing_count; | 1067 int best_partial_font_missing_char_count = INT_MAX; |
1016 Font best_partial_font = current_font; | 1068 Font best_partial_font = original_font; |
| 1069 Font current_font; |
1017 | 1070 |
1018 // Try to shape with the cached font from previous runs, if any. | 1071 run->logical_clusters.reset(new WORD[run_length]); |
1019 std::map<std::string, Font>::const_iterator it = | 1072 while (fonts.NextFont(¤t_font)) { |
1020 successful_substitute_fonts_.find(original_font.GetFontName()); | 1073 HRESULT hr = ShapeTextRunWithFont(run, current_font); |
1021 if (it != successful_substitute_fonts_.end()) { | 1074 |
1022 current_font = it->second; | 1075 bool glyphs_missing = false; |
1023 missing_count = CountCharsWithMissingGlyphs(run, | 1076 if (hr == USP_E_SCRIPT_NOT_IN_FONT) { |
1024 ShapeTextRunWithFont(run, current_font)); | 1077 glyphs_missing = true; |
1025 if (missing_count == 0) | 1078 } else if (hr == S_OK) { |
| 1079 // If |hr| is S_OK, there could still be missing glyphs in the output. |
| 1080 // http://msdn.microsoft.com/en-us/library/windows/desktop/dd368564.aspx |
| 1081 const int missing_count = CountCharsWithMissingGlyphs(run); |
| 1082 // Track the font that produced the least missing glyphs. |
| 1083 if (missing_count < best_partial_font_missing_char_count) { |
| 1084 best_partial_font_missing_char_count = missing_count; |
| 1085 best_partial_font = run->font; |
| 1086 } |
| 1087 glyphs_missing = (missing_count != 0); |
| 1088 } else { |
| 1089 NOTREACHED() << hr; |
| 1090 } |
| 1091 |
| 1092 // Use the font if it had glyphs for all characters. |
| 1093 if (!glyphs_missing) { |
| 1094 // Save the successful fallback font that was chosen. |
| 1095 if (tried_fallback) |
| 1096 successful_substitute_fonts_[original_font.GetFontName()] = run->font; |
1026 return; | 1097 return; |
1027 if (missing_count < best_partial_font_missing_char_count) { | 1098 } |
1028 best_partial_font_missing_char_count = missing_count; | 1099 |
1029 best_partial_font = current_font; | 1100 // First, try the cached font from previous runs, if any. |
| 1101 if (!tried_cached_font) { |
| 1102 tried_cached_font = true; |
| 1103 |
| 1104 std::map<std::string, Font>::const_iterator it = |
| 1105 successful_substitute_fonts_.find(original_font.GetFontName()); |
| 1106 if (it != successful_substitute_fonts_.end()) { |
| 1107 fonts.SetNextFont(it->second); |
| 1108 continue; |
| 1109 } |
| 1110 } |
| 1111 |
| 1112 // If there are missing glyphs, first try finding a fallback font using a |
| 1113 // meta file, if it hasn't yet been attempted for this run. |
| 1114 // TODO(msw|asvitkine): Support RenderText's font_list()? |
| 1115 if (!tried_fallback) { |
| 1116 tried_fallback = true; |
| 1117 |
| 1118 Font fallback_font; |
| 1119 if (ChooseFallbackFont(cached_hdc_, run->font, run_text, run_length, |
| 1120 &fallback_font)) { |
| 1121 fonts.SetNextFont(fallback_font); |
| 1122 continue; |
| 1123 } |
1030 } | 1124 } |
1031 } | 1125 } |
1032 | 1126 |
1033 // Try finding a fallback font using a meta file. | |
1034 // TODO(msw|asvitkine): Support RenderText's font_list()? | |
1035 if (GetUniscribeFallbackFont(original_font, run_text, run_length, | |
1036 ¤t_font)) { | |
1037 missing_count = CountCharsWithMissingGlyphs(run, | |
1038 ShapeTextRunWithFont(run, current_font)); | |
1039 if (missing_count == 0) { | |
1040 successful_substitute_fonts_[original_font.GetFontName()] = current_font; | |
1041 return; | |
1042 } | |
1043 if (missing_count < best_partial_font_missing_char_count) { | |
1044 best_partial_font_missing_char_count = missing_count; | |
1045 best_partial_font = current_font; | |
1046 } | |
1047 } | |
1048 | |
1049 // Try fonts in the fallback list except the first, which is |original_font|. | |
1050 std::vector<std::string> fonts = | |
1051 GetFallbackFontFamilies(original_font.GetFontName()); | |
1052 for (size_t i = 1; i < fonts.size(); ++i) { | |
1053 missing_count = CountCharsWithMissingGlyphs(run, | |
1054 ShapeTextRunWithFont(run, Font(fonts[i], original_font.GetFontSize()))); | |
1055 if (missing_count == 0) { | |
1056 successful_substitute_fonts_[original_font.GetFontName()] = current_font; | |
1057 return; | |
1058 } | |
1059 if (missing_count < best_partial_font_missing_char_count) { | |
1060 best_partial_font_missing_char_count = missing_count; | |
1061 best_partial_font = current_font; | |
1062 } | |
1063 } | |
1064 | |
1065 // If a font was able to partially display the run, use that now. | 1127 // If a font was able to partially display the run, use that now. |
1066 if (best_partial_font_missing_char_count < static_cast<int>(run_length)) { | 1128 if (best_partial_font_missing_char_count < static_cast<int>(run_length)) { |
1067 // Re-shape the run only if |best_partial_font| differs from the last font. | 1129 // Re-shape the run only if |best_partial_font| differs from the last font. |
1068 if (best_partial_font.GetNativeFont() != run->font.GetNativeFont()) | 1130 if (best_partial_font.GetNativeFont() != run->font.GetNativeFont()) |
1069 ShapeTextRunWithFont(run, best_partial_font); | 1131 ShapeTextRunWithFont(run, best_partial_font); |
1070 return; | 1132 return; |
1071 } | 1133 } |
1072 | 1134 |
1073 // If no font was able to partially display the run, replace all glyphs | 1135 // If no font was able to partially display the run, replace all glyphs |
1074 // with |wgDefault| from the original font to ensure to they don't hold | 1136 // with |wgDefault| from the original font to ensure to they don't hold |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1142 hr = ScriptShape(cached_hdc_, &run->script_cache, run_text, run_length, | 1204 hr = ScriptShape(cached_hdc_, &run->script_cache, run_text, run_length, |
1143 max_glyphs, &run->script_analysis, run->glyphs.get(), | 1205 max_glyphs, &run->script_analysis, run->glyphs.get(), |
1144 run->logical_clusters.get(), run->visible_attributes.get(), | 1206 run->logical_clusters.get(), run->visible_attributes.get(), |
1145 &run->glyph_count); | 1207 &run->glyph_count); |
1146 // Ensure that |kMaxGlyphs| is attempted and the loop terminates afterward. | 1208 // Ensure that |kMaxGlyphs| is attempted and the loop terminates afterward. |
1147 max_glyphs = std::max(max_glyphs + 1, std::min(max_glyphs * 2, kMaxGlyphs)); | 1209 max_glyphs = std::max(max_glyphs + 1, std::min(max_glyphs * 2, kMaxGlyphs)); |
1148 } | 1210 } |
1149 return hr; | 1211 return hr; |
1150 } | 1212 } |
1151 | 1213 |
1152 int RenderTextWin::CountCharsWithMissingGlyphs(internal::TextRun* run, | 1214 int RenderTextWin::CountCharsWithMissingGlyphs(internal::TextRun* run) const { |
1153 HRESULT shaping_result) const { | |
1154 if (shaping_result != S_OK) { | |
1155 DCHECK_EQ(shaping_result, USP_E_SCRIPT_NOT_IN_FONT); | |
1156 return INT_MAX; | |
1157 } | |
1158 | |
1159 // If |hr| is S_OK, there could still be missing glyphs in the output. | |
1160 // http://msdn.microsoft.com/en-us/library/windows/desktop/dd368564.aspx | |
1161 int chars_not_missing_glyphs = 0; | 1215 int chars_not_missing_glyphs = 0; |
1162 SCRIPT_FONTPROPERTIES properties; | 1216 SCRIPT_FONTPROPERTIES properties; |
1163 memset(&properties, 0, sizeof(properties)); | 1217 memset(&properties, 0, sizeof(properties)); |
1164 properties.cBytes = sizeof(properties); | 1218 properties.cBytes = sizeof(properties); |
1165 ScriptGetFontProperties(cached_hdc_, &run->script_cache, &properties); | 1219 ScriptGetFontProperties(cached_hdc_, &run->script_cache, &properties); |
1166 | 1220 |
1167 const wchar_t* run_text = &(GetLayoutText()[run->range.start()]); | 1221 const wchar_t* run_text = &(GetLayoutText()[run->range.start()]); |
1168 for (size_t char_index = 0; char_index < run->range.length(); ++char_index) { | 1222 for (size_t char_index = 0; char_index < run->range.length(); ++char_index) { |
1169 const int glyph_index = run->logical_clusters[char_index]; | 1223 const int glyph_index = run->logical_clusters[char_index]; |
1170 DCHECK_GE(glyph_index, 0); | 1224 DCHECK_GE(glyph_index, 0); |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1224 size_t position = LayoutIndexToTextIndex(run->range.end()); | 1278 size_t position = LayoutIndexToTextIndex(run->range.end()); |
1225 position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD); | 1279 position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD); |
1226 return SelectionModel(position, CURSOR_FORWARD); | 1280 return SelectionModel(position, CURSOR_FORWARD); |
1227 } | 1281 } |
1228 | 1282 |
1229 RenderText* RenderText::CreateNativeInstance() { | 1283 RenderText* RenderText::CreateNativeInstance() { |
1230 return new RenderTextWin; | 1284 return new RenderTextWin; |
1231 } | 1285 } |
1232 | 1286 |
1233 } // namespace gfx | 1287 } // namespace gfx |
OLD | NEW |