繼續分析如下步驟windows
一.TextRun結構api
struct TextRun {
TextRun();
~TextRun();
ui::Range range;
Font font;
// A gfx::Font::FontStyle flag to specify bold and italic styles. // Supersedes |font.GetFontStyle()|. Stored separately to avoid calling // |font.DeriveFont()|, which is expensive on Windows. int font_style;
// TODO(msw): Disambiguate color/style from TextRuns for proper glyph shaping. // See an example: http://www.catch22.net/tuts/uniscribe-mysteries SkColor foreground;
bool strike;
bool diagonal_strike;
bool underline;
int width;
// The cumulative widths of preceding runs. int preceding_run_widths;
SCRIPT_ANALYSIS script_analysis;
scoped_ptr<WORD[]> glyphs;
scoped_ptr<WORD[]> logical_clusters;
scoped_ptr<SCRIPT_VISATTR[]> visible_attributes;
int glyph_count;
scoped_ptr<int[]> advance_widths;
scoped_ptr<GOFFSET[]> offsets;
ABC abc_widths;
SCRIPT_CACHE script_cache;
private:
DISALLOW_COPY_AND_ASSIGN(TextRun);
};
TextRun能夠理解爲一個輸出結果字體
二.ScriptShapeui
void RenderTextWin::LayoutTextRun(internal::TextRun* run) {
const size_t run_length = run->range.length();
const wchar_t* run_text = &(GetLayoutText()[run->range.start()]);
Font original_font = run->font;
LinkedFontsIterator fonts(original_font);
bool tried_cached_font = false;
bool tried_fallback = false;
// Keep track of the font that is able to display the greatest number of // characters for which ScriptShape() returned S_OK. This font will be used // in the case where no font is able to display the entire run. int best_partial_font_missing_char_count = INT_MAX;
Font best_partial_font = original_font;
bool using_best_partial_font = false;
Font current_font;
run->logical_clusters.reset(new WORD[run_length]);
while (fonts.NextFont(¤t_font)) {
HRESULT hr = ShapeTextRunWithFont(run, current_font);
bool glyphs_missing = false;
if (hr == USP_E_SCRIPT_NOT_IN_FONT) {
glyphs_missing = true;
} else if (hr == S_OK) {
// If |hr| is S_OK, there could still be missing glyphs in the output. // http://msdn.microsoft.com/en-us/library/windows/desktop/dd368564.aspx const int missing_count = CountCharsWithMissingGlyphs(run);
// Track the font that produced the least missing glyphs. if (missing_count < best_partial_font_missing_char_count) {
best_partial_font_missing_char_count = missing_count;
best_partial_font = run->font;
}
glyphs_missing = (missing_count != 0);
} else {
NOTREACHED() << hr;
}
// Use the font if it had glyphs for all characters. if (!glyphs_missing) {
// Save the successful fallback font that was chosen. if (tried_fallback)
successful_substitute_fonts_[original_font.GetFontName()] = run->font;
return;
}
// First, try the cached font from previous runs, if any. if (!tried_cached_font) {
tried_cached_font = true;
std::map<std::string, Font>::const_iterator it =
successful_substitute_fonts_.find(original_font.GetFontName());
if (it != successful_substitute_fonts_.end()) {
fonts.SetNextFont(it->second);
continue;
}
}
// If there are missing glyphs, first try finding a fallback font using a // meta file, if it hasn't yet been attempted for this run. // TODO(msw|asvitkine): Support RenderText's font_list()? if (!tried_fallback) {
tried_fallback = true;
Font fallback_font;
if (ChooseFallbackFont(cached_hdc_, run->font, run_text, run_length,
&fallback_font)) {
fonts.SetNextFont(fallback_font);
continue;
}
}
}
// If a font was able to partially display the run, use that now. if (best_partial_font_missing_char_count < static_cast<int>(run_length)) {
// Re-shape the run only if |best_partial_font| differs from the last font. if (best_partial_font.GetNativeFont() != run->font.GetNativeFont())
ShapeTextRunWithFont(run, best_partial_font);
return;
}
// If no font was able to partially display the run, replace all glyphs // with |wgDefault| from the original font to ensure to they don't hold // garbage values. // First, clear the cache and select the original font on the HDC. ScriptFreeCache(&run->script_cache); run->font = original_font; SelectObject(cached_hdc_, run->font.GetNativeFont()); // Now, get the font's properties. SCRIPT_FONTPROPERTIES properties; memset(&properties, 0, sizeof(properties)); properties.cBytes = sizeof(properties); HRESULT hr = ScriptGetFontProperties(cached_hdc_, &run->script_cache, &properties); if (hr == S_OK) { // Finally, initialize |glyph_count|, |glyphs| and |visible_attributes| on // the run (since they may not have been set yet). run->glyph_count = run_length; memset(run->visible_attributes.get(), 0, run->glyph_count * sizeof(SCRIPT_VISATTR)); for (int i = 0; i < run->glyph_count; ++i) { run->glyphs[i] = IsWhitespace(run_text[i]) ? properties.wgBlank : properties.wgDefault; } } // TODO(msw): Don't use SCRIPT_UNDEFINED. Apparently Uniscribe can // crash on certain surrogate pairs with SCRIPT_UNDEFINED. // See https://bugzilla.mozilla.org/show_bug.cgi?id=341500 // And http://maxradi.us/documents/uniscribe/ run->script_analysis.eScript = SCRIPT_UNDEFINED;
}
HRESULT RenderTextWin::ShapeTextRunWithFont(internal::TextRun* run, const Font& font) { // Update the run's font only if necessary. If the two fonts wrap the same // PlatformFontWin object, their native fonts will have the same value. if (run->font.GetNativeFont() != font.GetNativeFont()) { const int font_size = run->font.GetFontSize(); const int font_height = run->font.GetHeight(); run->font = font; DeriveFontIfNecessary(font_size, font_height, run->font_style, &run->font); ScriptFreeCache(&run->script_cache); } // Select the font desired for glyph generation. SelectObject(cached_hdc_, run->font.GetNativeFont()); HRESULT hr = E_OUTOFMEMORY; const size_t run_length = run->range.length(); const wchar_t* run_text = &(GetLayoutText()[run->range.start()]); // Max glyph guess: http://msdn.microsoft.com/en-us/library/dd368564.aspx size_t max_glyphs = static_cast<size_t>(1.5 * run_length + 16); while (hr == E_OUTOFMEMORY && max_glyphs < kMaxGlyphs) { run->glyph_count = 0; run->glyphs.reset(new WORD[max_glyphs]); run->visible_attributes.reset(new SCRIPT_VISATTR[max_glyphs]); hr = ScriptShape(cached_hdc_, &run->script_cache, run_text, run_length, max_glyphs, &run->script_analysis, run->glyphs.get(), run->logical_clusters.get(), run->visible_attributes.get(), &run->glyph_count); max_glyphs *= 2; } return hr; }
三.ScriptPlacethis
run->abc_widths是計算的結果spa
void RenderTextWin::LayoutVisualText() {
DCHECK(!runs_.empty());
if (!cached_hdc_)
cached_hdc_ = CreateCompatibleDC(NULL);
HRESULT hr = E_FAIL;
string_size_.set_height(0);
for (size_t i = 0; i < runs_.size(); ++i) {
internal::TextRun* run = runs_[i];
LayoutTextRun(run);
string_size_.set_height(std::max(string_size_.height(),
run->font.GetHeight()));
common_baseline_ = std::max(common_baseline_, run->font.GetBaseline());
if (run->glyph_count > 0) {
run->advance_widths.reset(new int[run->glyph_count]); run->offsets.reset(new GOFFSET[run->glyph_count]); hr = ScriptPlace(cached_hdc_, &run->script_cache, run->glyphs.get(), run->glyph_count, run->visible_attributes.get(), &(run->script_analysis), run->advance_widths.get(), run->offsets.get(), &(run->abc_widths)); DCHECK(SUCCEEDED(hr)); }
}
// Build the array of bidirectional embedding levels. scoped_ptr<BYTE[]> levels(new BYTE[runs_.size()]);
for (size_t i = 0; i < runs_.size(); ++i)
levels[i] = runs_[i]->script_analysis.s.uBidiLevel;
// Get the maps between visual and logical run indices. visual_to_logical_.reset(new int[runs_.size()]);
logical_to_visual_.reset(new int[runs_.size()]);
hr = ScriptLayout(runs_.size(),
levels.get(),
visual_to_logical_.get(),
logical_to_visual_.get());
DCHECK(SUCCEEDED(hr));
// Precalculate run width information. size_t preceding_run_widths = 0;
for (size_t i = 0; i < runs_.size(); ++i) {
internal::TextRun* run = runs_[visual_to_logical_[i]];
run->preceding_run_widths = preceding_run_widths;
const ABC& abc = run->abc_widths;
run->width = abc.abcA + abc.abcB + abc.abcC;
preceding_run_widths += run->width;
}
string_size_.set_width(preceding_run_widths);
}