diff options
author | derat <derat@chromium.org> | 2015-02-02 17:59:08 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-02-03 02:00:01 +0000 |
commit | a55b0115d7f52cd51a309eca15b08aa68d139455 (patch) | |
tree | 334298574010be0059b562e396a873d4ae934412 | |
parent | 2e9885788692f69203a98fb42347428ef553d640 (diff) | |
download | chromium_src-a55b0115d7f52cd51a309eca15b08aa68d139455.zip chromium_src-a55b0115d7f52cd51a309eca15b08aa68d139455.tar.gz chromium_src-a55b0115d7f52cd51a309eca15b08aa68d139455.tar.bz2 |
Delete RenderTextWin and RenderTextPango.
Windows, Linux, and Chrome OS use the cross-platform
RenderTextHarfBuzz class as of M40. Remove the
platform-specific RenderText implementations.
Also update chrome://flags so that only Mac users can
control whether RenderTextHarfBuzz is used. RenderTextMac is
still used there by default.
BUG=321868,398885
Review URL: https://codereview.chromium.org/891013003
Cr-Commit-Position: refs/heads/master@{#314242}
-rw-r--r-- | chrome/app/generated_resources.grd | 8 | ||||
-rw-r--r-- | chrome/browser/about_flags.cc | 17 | ||||
-rw-r--r-- | ui/gfx/BUILD.gn | 15 | ||||
-rw-r--r-- | ui/gfx/gfx.gyp | 6 | ||||
-rw-r--r-- | ui/gfx/render_text.cc | 23 | ||||
-rw-r--r-- | ui/gfx/render_text.h | 3 | ||||
-rw-r--r-- | ui/gfx/render_text_mac.cc | 4 | ||||
-rw-r--r-- | ui/gfx/render_text_ozone.cc | 13 | ||||
-rw-r--r-- | ui/gfx/render_text_pango.cc | 535 | ||||
-rw-r--r-- | ui/gfx/render_text_pango.h | 89 | ||||
-rw-r--r-- | ui/gfx/render_text_unittest.cc | 138 | ||||
-rw-r--r-- | ui/gfx/render_text_win.cc | 1261 | ||||
-rw-r--r-- | ui/gfx/render_text_win.h | 147 | ||||
-rw-r--r-- | ui/gfx/switches.cc | 12 | ||||
-rw-r--r-- | ui/gfx/switches.h | 6 | ||||
-rw-r--r-- | ui/gfx/win/direct_write.cc | 4 |
16 files changed, 49 insertions, 2232 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index cec3910..f613bf9 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -6710,11 +6710,11 @@ Keep your key file in a safe place. You will need it to create new versions of y <message name="IDS_FLAGS_TEXT_INPUT_FOCUS_MANAGER_DESCRIPTION" desc="Description for the flag to enable the new text input focus manager."> Enable an experimental focus manager to track text input clients. </message> - <message name="IDS_FLAGS_HARFBUZZ_RENDERTEXT_NAME" desc="Name of the about:flags HarfBuzz RenderText experiment."> - HarfBuzz for UI text. + <message name="IDS_FLAGS_ENABLE_HARFBUZZ_RENDERTEXT_NAME" desc="Name of the about:flags HarfBuzz RenderText experiment."> + Enable HarfBuzz for UI text. </message> - <message name="IDS_FLAGS_HARFBUZZ_RENDERTEXT_DESCRIPTION" desc="Description of the about:flags HarfBuzz RenderText experiment."> - Cross-platform HarfBuzz engine for UI text. Doesn't affect web content. + <message name="IDS_FLAGS_ENABLE_HARFBUZZ_RENDERTEXT_DESCRIPTION" desc="Description of the about:flags HarfBuzz RenderText experiment."> + Enable cross-platform HarfBuzz layout engine for UI text. Doesn't affect web content. </message> <if expr="is_android"> <message name="IDS_FLAGS_ENABLE_ANSWERS_IN_SUGGEST_NAME" desc="Name of the about:flags Answers in Suggest experiment."> diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index ac631eb..b002070 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc @@ -1872,14 +1872,6 @@ const Experiment kExperiments[] = { SINGLE_VALUE_TYPE(extensions::switches::kEnableScriptsRequireAction) }, #endif - { - "harfbuzz-rendertext", - IDS_FLAGS_HARFBUZZ_RENDERTEXT_NAME, - IDS_FLAGS_HARFBUZZ_RENDERTEXT_DESCRIPTION, - kOsDesktop, - ENABLE_DISABLE_VALUE_TYPE(switches::kEnableHarfBuzzRenderText, - switches::kDisableHarfBuzzRenderText) - }, #if defined(OS_ANDROID) { "answers-in-suggest", @@ -2145,6 +2137,15 @@ const Experiment kExperiments[] = { kOsAll, SINGLE_VALUE_TYPE(switches::kSitePerProcess) }, +#if defined(OS_MACOSX) + { + "enable-harfbuzz-rendertext", + IDS_FLAGS_ENABLE_HARFBUZZ_RENDERTEXT_NAME, + IDS_FLAGS_ENABLE_HARFBUZZ_RENDERTEXT_DESCRIPTION, + kOsMac, + SINGLE_VALUE_TYPE(switches::kEnableHarfBuzzRenderText) + }, +#endif // defined(OS_MACOSX) #if defined(OS_CHROMEOS) { "enable-timezone-tracking", diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn index e9ce735..0c3b42d 100644 --- a/ui/gfx/BUILD.gn +++ b/ui/gfx/BUILD.gn @@ -242,25 +242,16 @@ component("gfx") { if (is_android || is_ios) { # We don't support RenderText on these platforms. } else { - # These text rendering files are supported everywhere text rendering is. + # Mac doesn't use RenderTextHarfBuzz by default yet. sources += [ "render_text.cc", "render_text.h", "render_text_harfbuzz.cc", "render_text_harfbuzz.h", + "render_text_mac.cc", + "render_text_mac.h", "text_utils_skia.cc", ] - - # These are the "native" rendering routines, only one should apply. - if (is_win) { - sources += [ "render_text_win.cc" ] - } else if (is_mac) { - sources += [ "render_text_mac.cc" ] - } else if (use_pango) { - sources += [ "render_text_pango.cc" ] - } else if (use_ozone) { - sources += [ "render_text_ozone.cc" ] - } } # iOS. diff --git a/ui/gfx/gfx.gyp b/ui/gfx/gfx.gyp index 6fba50a..274c5dd 100644 --- a/ui/gfx/gfx.gyp +++ b/ui/gfx/gfx.gyp @@ -247,11 +247,6 @@ 'render_text_harfbuzz.h', 'render_text_mac.cc', 'render_text_mac.h', - 'render_text_ozone.cc', - 'render_text_pango.cc', - 'render_text_pango.h', - 'render_text_win.cc', - 'render_text_win.h', 'scoped_canvas.h', 'scoped_cg_context_save_gstate_mac.h', 'scoped_ns_graphics_context_save_gstate_mac.h', @@ -407,7 +402,6 @@ ], 'sources!': [ 'platform_font_ozone.cc', - 'render_text_ozone.cc', ], }], ['desktop_linux==1 or chromeos==1', { diff --git a/ui/gfx/render_text.cc b/ui/gfx/render_text.cc index 59742cc..9ef5750 100644 --- a/ui/gfx/render_text.cc +++ b/ui/gfx/render_text.cc @@ -28,6 +28,10 @@ #include "ui/gfx/text_utils.h" #include "ui/gfx/utf16_indexing.h" +#if defined(OS_MACOSX) +#include "ui/gfx/render_text_mac.h" +#endif // defined(OS_MACOSX) + namespace gfx { namespace { @@ -400,22 +404,17 @@ RenderText::~RenderText() { RenderText* RenderText::CreateInstance() { #if defined(OS_MACOSX) - static const bool use_harfbuzz = - base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableHarfBuzzRenderText); -#else - static const bool use_harfbuzz = + static const bool use_native = !base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableHarfBuzzRenderText); -#endif - return use_harfbuzz ? new RenderTextHarfBuzz : CreateNativeInstance(); + switches::kEnableHarfBuzzRenderText); + if (use_native) + return new RenderTextMac; +#endif // defined(OS_MACOSX) + return new RenderTextHarfBuzz; } RenderText* RenderText::CreateInstanceForEditing() { - static const bool use_harfbuzz = - !base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableHarfBuzzRenderText); - return use_harfbuzz ? new RenderTextHarfBuzz : CreateNativeInstance(); + return new RenderTextHarfBuzz; } void RenderText::SetText(const base::string16& text) { diff --git a/ui/gfx/render_text.h b/ui/gfx/render_text.h index 8f6fa27..415132f 100644 --- a/ui/gfx/render_text.h +++ b/ui/gfx/render_text.h @@ -590,9 +590,6 @@ class GFX_EXPORT RenderText { FRIEND_TEST_ALL_PREFIXES(RenderTextTest, PangoAttributes); FRIEND_TEST_ALL_PREFIXES(RenderTextTest, StringFitsOwnWidth); - // Creates a platform-specific RenderText instance. - static RenderText* CreateNativeInstance(); - // Set the cursor to |position|, with the caret trailing the previous // grapheme, or if there is no previous grapheme, leading the cursor position. // If |select| is false, the selection start is moved to the same position. diff --git a/ui/gfx/render_text_mac.cc b/ui/gfx/render_text_mac.cc index 912382f..c98d744 100644 --- a/ui/gfx/render_text_mac.cc +++ b/ui/gfx/render_text_mac.cc @@ -343,8 +343,4 @@ void RenderTextMac::ComputeRuns() { runs_valid_ = true; } -RenderText* RenderText::CreateNativeInstance() { - return new RenderTextMac; -} - } // namespace gfx diff --git a/ui/gfx/render_text_ozone.cc b/ui/gfx/render_text_ozone.cc deleted file mode 100644 index fb5ef99..0000000 --- a/ui/gfx/render_text_ozone.cc +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) 2013 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 "ui/gfx/render_text.h" - -namespace gfx { - -RenderText* RenderText::CreateNativeInstance() { - return NULL; -} - -} // namespace gfx diff --git a/ui/gfx/render_text_pango.cc b/ui/gfx/render_text_pango.cc deleted file mode 100644 index c90af56..0000000 --- a/ui/gfx/render_text_pango.cc +++ /dev/null @@ -1,535 +0,0 @@ -// Copyright (c) 2012 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 "ui/gfx/render_text_pango.h" - -#include <pango/pangocairo.h> -#include <algorithm> -#include <string> -#include <vector> - -#include "base/i18n/break_iterator.h" -#include "base/logging.h" -#include "third_party/skia/include/core/SkTypeface.h" -#include "ui/gfx/canvas.h" -#include "ui/gfx/font.h" -#include "ui/gfx/font_list.h" -#include "ui/gfx/font_render_params.h" -#include "ui/gfx/pango_util.h" -#include "ui/gfx/platform_font_pango.h" -#include "ui/gfx/utf16_indexing.h" - -namespace gfx { - -namespace { - -// Returns the preceding element in a GSList (O(n)). -GSList* GSListPrevious(GSList* head, GSList* item) { - GSList* prev = NULL; - for (GSList* cur = head; cur != item; cur = cur->next) { - DCHECK(cur); - prev = cur; - } - return prev; -} - -// Returns true if the given visual cursor |direction| is logically forward -// motion in the given Pango |item|. -bool IsForwardMotion(VisualCursorDirection direction, const PangoItem* item) { - bool rtl = item->analysis.level & 1; - return rtl == (direction == CURSOR_LEFT); -} - -// Checks whether |range| contains |index|. This is not the same as calling -// range.Contains(Range(index)), which returns true if |index| == |range.end()|. -bool IndexInRange(const Range& range, size_t index) { - return index >= range.start() && index < range.end(); -} - -// Sets underline metrics on |renderer| according to Pango font |desc|. -void SetPangoUnderlineMetrics(PangoFontDescription *desc, - internal::SkiaTextRenderer* renderer) { - PangoFontMetrics* metrics = GetPangoFontMetrics(desc); - int thickness = pango_font_metrics_get_underline_thickness(metrics); - // Pango returns the position "above the baseline". Change its sign to convert - // it to a vertical offset from the baseline. - int position = -pango_font_metrics_get_underline_position(metrics); - - // Note: pango_quantize_line_geometry() guarantees pixel boundaries, so - // PANGO_PIXELS() is safe to use. - pango_quantize_line_geometry(&thickness, &position); - int thickness_pixels = PANGO_PIXELS(thickness); - int position_pixels = PANGO_PIXELS(position); - - // Ugly hack: make sure that underlines don't get clipped. See - // http://crbug.com/393117. - int descent_pixels = PANGO_PIXELS(pango_font_metrics_get_descent(metrics)); - position_pixels = std::min(position_pixels, - descent_pixels - thickness_pixels); - - renderer->SetUnderlineMetrics(thickness_pixels, position_pixels); -} - -} // namespace - -// TODO(xji): index saved in upper layer is utf16 index. Pango uses utf8 index. -// Since caret_pos is used internally, we could save utf8 index for caret_pos -// to avoid conversion. - -RenderTextPango::RenderTextPango() - : layout_(NULL), - current_line_(NULL), - log_attrs_(NULL), - num_log_attrs_(0), - layout_text_(NULL) { -} - -RenderTextPango::~RenderTextPango() { - ResetLayout(); -} - -scoped_ptr<RenderText> RenderTextPango::CreateInstanceOfSameType() const { - return scoped_ptr<RenderTextPango>(new RenderTextPango); -} - -Size RenderTextPango::GetStringSize() { - EnsureLayout(); - int width = 0, height = 0; - pango_layout_get_pixel_size(layout_, &width, &height); - - // Pango returns 0 widths for very long strings (of 0x40000 chars or more). - // This is caused by an int overflow in pango_glyph_string_extents_range. - // Absurdly long strings may even report non-zero garbage values for width; - // while detecting that isn't worthwhile, this handles the 0 width cases. - const long kAbsurdLength = 100000; - if (width == 0 && g_utf8_strlen(layout_text_, -1) > kAbsurdLength) - width = font_list().GetExpectedTextWidth(g_utf8_strlen(layout_text_, -1)); - - // Keep a consistent height between this particular string's PangoLayout and - // potentially larger text supported by the FontList. - // For example, if a text field contains a Japanese character, which is - // smaller than Latin ones, and then later a Latin one is inserted, this - // ensures that the text baseline does not shift. - return Size(width, std::max(height, font_list().GetHeight())); -} - -SelectionModel RenderTextPango::FindCursorPosition(const Point& point) { - EnsureLayout(); - - if (text().empty()) - return SelectionModel(0, CURSOR_FORWARD); - - Point p(ToTextPoint(point)); - - // When the point is outside of text, return HOME/END position. - if (p.x() < 0) - return EdgeSelectionModel(CURSOR_LEFT); - if (p.x() > GetStringSize().width()) - return EdgeSelectionModel(CURSOR_RIGHT); - - int caret_pos = 0, trailing = 0; - pango_layout_xy_to_index(layout_, p.x() * PANGO_SCALE, p.y() * PANGO_SCALE, - &caret_pos, &trailing); - - DCHECK_GE(trailing, 0); - if (trailing > 0) { - caret_pos = g_utf8_offset_to_pointer(layout_text_ + caret_pos, - trailing) - layout_text_; - DCHECK_LE(static_cast<size_t>(caret_pos), strlen(layout_text_)); - } - - return SelectionModel(LayoutIndexToTextIndex(caret_pos), - (trailing > 0) ? CURSOR_BACKWARD : CURSOR_FORWARD); -} - -std::vector<RenderText::FontSpan> RenderTextPango::GetFontSpansForTesting() { - EnsureLayout(); - - std::vector<RenderText::FontSpan> spans; - for (GSList* it = current_line_->runs; it; it = it->next) { - PangoItem* item = reinterpret_cast<PangoLayoutRun*>(it->data)->item; - const int start = LayoutIndexToTextIndex(item->offset); - const int end = LayoutIndexToTextIndex(item->offset + item->length); - const Range range(start, end); - - ScopedPangoFontDescription desc(pango_font_describe(item->analysis.font)); - spans.push_back(RenderText::FontSpan(Font(desc.get()), range)); - } - - return spans; -} - -int RenderTextPango::GetLayoutTextBaseline() { - EnsureLayout(); - return PANGO_PIXELS(pango_layout_get_baseline(layout_)); -} - -SelectionModel RenderTextPango::AdjacentCharSelectionModel( - const SelectionModel& selection, - VisualCursorDirection direction) { - GSList* run = GetRunContainingCaret(selection); - if (!run) { - // The cursor is not in any run: we're at the visual and logical edge. - SelectionModel edge = EdgeSelectionModel(direction); - if (edge.caret_pos() == selection.caret_pos()) - return edge; - else - run = (direction == CURSOR_RIGHT) ? - current_line_->runs : g_slist_last(current_line_->runs); - } else { - // If the cursor is moving within the current run, just move it by one - // grapheme in the appropriate direction. - PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item; - size_t caret = selection.caret_pos(); - if (IsForwardMotion(direction, item)) { - if (caret < LayoutIndexToTextIndex(item->offset + item->length)) { - caret = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD); - return SelectionModel(caret, CURSOR_BACKWARD); - } - } else { - if (caret > LayoutIndexToTextIndex(item->offset)) { - caret = IndexOfAdjacentGrapheme(caret, CURSOR_BACKWARD); - return SelectionModel(caret, CURSOR_FORWARD); - } - } - // The cursor is at the edge of a run; move to the visually adjacent run. - // TODO(xji): Keep a vector of runs to avoid using a singly-linked list. - run = (direction == CURSOR_RIGHT) ? - run->next : GSListPrevious(current_line_->runs, run); - if (!run) - return EdgeSelectionModel(direction); - } - PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item; - return IsForwardMotion(direction, item) ? - FirstSelectionModelInsideRun(item) : LastSelectionModelInsideRun(item); -} - -SelectionModel RenderTextPango::AdjacentWordSelectionModel( - const SelectionModel& selection, - VisualCursorDirection direction) { - if (obscured()) - return EdgeSelectionModel(direction); - - base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); - bool success = iter.Init(); - DCHECK(success); - if (!success) - return selection; - - SelectionModel cur(selection); - for (;;) { - cur = AdjacentCharSelectionModel(cur, direction); - GSList* run = GetRunContainingCaret(cur); - if (!run) - break; - PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item; - size_t cursor = cur.caret_pos(); - if (IsForwardMotion(direction, item) ? - iter.IsEndOfWord(cursor) : iter.IsStartOfWord(cursor)) - break; - } - - return cur; -} - -Range RenderTextPango::GetGlyphBounds(size_t index) { - EnsureLayout(); - PangoRectangle pos; - pango_layout_index_to_pos(layout_, TextIndexToLayoutIndex(index), &pos); - // TODO(derat): Support fractional ranges for subpixel positioning? - return Range(PANGO_PIXELS(pos.x), PANGO_PIXELS(pos.x + pos.width)); -} - -std::vector<Rect> RenderTextPango::GetSubstringBounds(const Range& range) { - DCHECK_LE(range.GetMax(), text().length()); - if (range.is_empty()) - return std::vector<Rect>(); - - EnsureLayout(); - int* ranges = NULL; - int n_ranges = 0; - pango_layout_line_get_x_ranges(current_line_, - TextIndexToLayoutIndex(range.GetMin()), - TextIndexToLayoutIndex(range.GetMax()), - &ranges, - &n_ranges); - - const int height = GetStringSize().height(); - - std::vector<Rect> bounds; - for (int i = 0; i < n_ranges; ++i) { - // TODO(derat): Support fractional bounds for subpixel positioning? - int x = PANGO_PIXELS(ranges[2 * i]); - int width = PANGO_PIXELS(ranges[2 * i + 1]) - x; - Rect rect(x, 0, width, height); - rect.set_origin(ToViewPoint(rect.origin())); - bounds.push_back(rect); - } - g_free(ranges); - return bounds; -} - -size_t RenderTextPango::TextIndexToLayoutIndex(size_t index) const { - DCHECK(layout_); - ptrdiff_t offset = UTF16IndexToOffset(text(), 0, index); - // Clamp layout indices to the length of the text actually used for layout. - offset = std::min<size_t>(offset, g_utf8_strlen(layout_text_, -1)); - const char* layout_pointer = g_utf8_offset_to_pointer(layout_text_, offset); - return (layout_pointer - layout_text_); -} - -size_t RenderTextPango::LayoutIndexToTextIndex(size_t index) const { - DCHECK(layout_); - const char* layout_pointer = layout_text_ + index; - const long offset = g_utf8_pointer_to_offset(layout_text_, layout_pointer); - return UTF16OffsetToIndex(text(), 0, offset); -} - -bool RenderTextPango::IsValidCursorIndex(size_t index) { - if (index == 0 || index == text().length()) - return true; - if (!IsValidLogicalIndex(index)) - return false; - - EnsureLayout(); - ptrdiff_t offset = UTF16IndexToOffset(text(), 0, index); - // Check that the index is marked as a legitimate cursor position by Pango. - return offset < num_log_attrs_ && log_attrs_[offset].is_cursor_position; -} - -void RenderTextPango::ResetLayout() { - // set_cached_bounds_and_offset_valid(false) is done in RenderText for every - // operation that triggers ResetLayout(). - if (layout_) { - // TODO(msw): Keep |layout_| across text changes, etc.; it can be re-used. - g_object_unref(layout_); - layout_ = NULL; - } - if (current_line_) { - pango_layout_line_unref(current_line_); - current_line_ = NULL; - } - if (log_attrs_) { - g_free(log_attrs_); - log_attrs_ = NULL; - num_log_attrs_ = 0; - } - layout_text_ = NULL; -} - -void RenderTextPango::EnsureLayout() { - if (layout_ == NULL) { - cairo_surface_t* surface = - cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0); - CHECK_EQ(CAIRO_STATUS_SUCCESS, cairo_surface_status(surface)); - cairo_t* cr = cairo_create(surface); - CHECK_EQ(CAIRO_STATUS_SUCCESS, cairo_status(cr)); - - layout_ = pango_cairo_create_layout(cr); - CHECK_NE(static_cast<PangoLayout*>(NULL), layout_); - cairo_destroy(cr); - cairo_surface_destroy(surface); - - SetUpPangoLayout(layout_, GetLayoutText(), font_list(), GetTextDirection(), - Canvas::DefaultCanvasTextAlignment()); - - // No width set so that the x-axis position is relative to the start of the - // text. ToViewPoint and ToTextPoint take care of the position conversion - // between text space and view spaces. - pango_layout_set_width(layout_, -1); - // TODO(xji): If RenderText will be used for displaying purpose, such as - // label, we will need to remove the single-line-mode setting. - pango_layout_set_single_paragraph_mode(layout_, true); - - layout_text_ = pango_layout_get_text(layout_); - SetupPangoAttributes(layout_); - - current_line_ = pango_layout_get_line_readonly(layout_, 0); - CHECK_NE(static_cast<PangoLayoutLine*>(NULL), current_line_); - pango_layout_line_ref(current_line_); - - pango_layout_get_log_attrs(layout_, &log_attrs_, &num_log_attrs_); - } -} - -void RenderTextPango::SetupPangoAttributes(PangoLayout* layout) { - PangoAttrList* attrs = pango_attr_list_new(); - - // Splitting text runs to accommodate styling can break Arabic glyph shaping. - // Only split text runs as needed for bold and italic font styles changes. - BreakList<bool>::const_iterator bold = styles()[BOLD].breaks().begin(); - BreakList<bool>::const_iterator italic = styles()[ITALIC].breaks().begin(); - while (bold != styles()[BOLD].breaks().end() && - italic != styles()[ITALIC].breaks().end()) { - const int style = (bold->second ? Font::BOLD : 0) | - (italic->second ? Font::ITALIC : 0); - const size_t bold_end = styles()[BOLD].GetRange(bold).end(); - const size_t italic_end = styles()[ITALIC].GetRange(italic).end(); - const size_t style_end = std::min(bold_end, italic_end); - if (style != font_list().GetFontStyle()) { - // TODO(derat): Don't interpret gfx::FontList font descriptions as Pango - // font descriptions: http://crbug.com/393067 - FontList derived_font_list = font_list().DeriveWithStyle(style); - ScopedPangoFontDescription desc( - derived_font_list.GetFontDescriptionString()); - - PangoAttribute* pango_attr = pango_attr_font_desc_new(desc.get()); - pango_attr->start_index = - TextIndexToLayoutIndex(std::max(bold->first, italic->first)); - pango_attr->end_index = TextIndexToLayoutIndex(style_end); - pango_attr_list_insert(attrs, pango_attr); - } - bold += bold_end == style_end ? 1 : 0; - italic += italic_end == style_end ? 1 : 0; - } - DCHECK(bold == styles()[BOLD].breaks().end()); - DCHECK(italic == styles()[ITALIC].breaks().end()); - - pango_layout_set_attributes(layout, attrs); - pango_attr_list_unref(attrs); -} - -void RenderTextPango::DrawVisualText(Canvas* canvas) { - DCHECK(layout_); - - // Skia will draw glyphs with respect to the baseline. - Vector2d offset(GetLineOffset(0) + Vector2d(0, GetLayoutTextBaseline())); - - SkScalar x = SkIntToScalar(offset.x()); - SkScalar y = SkIntToScalar(offset.y()); - - std::vector<SkPoint> pos; - std::vector<uint16> glyphs; - - internal::SkiaTextRenderer renderer(canvas); - ApplyFadeEffects(&renderer); - ApplyTextShadows(&renderer); - renderer.SetFontRenderParams( - font_list().GetPrimaryFont().GetFontRenderParams(), - background_is_transparent()); - - // Temporarily apply composition underlines and selection colors. - ApplyCompositionAndSelectionStyles(); - - internal::StyleIterator style(colors(), styles()); - for (GSList* it = current_line_->runs; it; it = it->next) { - // Skip painting runs outside the display area. - if (SkScalarTruncToInt(x) >= display_rect().right()) - break; - - PangoLayoutRun* run = reinterpret_cast<PangoLayoutRun*>(it->data); - int glyph_count = run->glyphs->num_glyphs; - if (glyph_count == 0) - continue; - - ScopedPangoFontDescription desc( - pango_font_describe(run->item->analysis.font)); - const std::string family_name = - pango_font_description_get_family(desc.get()); - renderer.SetTextSize(GetPangoFontSizeInPixels(desc.get())); - - glyphs.resize(glyph_count); - pos.resize(glyph_count); - - // Track the current glyph and the glyph at the start of its styled range. - int glyph_index = 0; - int style_start_glyph_index = glyph_index; - - // Track the x-coordinates for each styled range (|x| marks the current). - SkScalar style_start_x = x; - - // Track the current style and its text (not layout) index range. - style.UpdatePosition(GetGlyphTextIndex(run, style_start_glyph_index)); - Range style_range = style.GetRange(); - - do { - const PangoGlyphInfo& glyph = run->glyphs->glyphs[glyph_index]; - glyphs[glyph_index] = static_cast<uint16>(glyph.glyph); - // Use pango_units_to_double() rather than PANGO_PIXELS() here, so units - // are not rounded to the pixel grid if subpixel positioning is enabled. - pos[glyph_index].set(x + pango_units_to_double(glyph.geometry.x_offset), - y + pango_units_to_double(glyph.geometry.y_offset)); - x += pango_units_to_double(glyph.geometry.width); - - ++glyph_index; - // If this is the last glyph of the range or the last glyph inside the - // display area (which would cause early termination of the loop), paint - // the range. - const size_t glyph_text_index = (glyph_index == glyph_count) ? - style_range.end() : GetGlyphTextIndex(run, glyph_index); - if (!IndexInRange(style_range, glyph_text_index) || - SkScalarTruncToInt(x) >= display_rect().right()) { - // TODO(asvitkine): For cases like "fi", where "fi" is a single glyph - // but can span multiple styles, Pango splits the - // styles evenly over the glyph. We can do this too by - // clipping and drawing the glyph several times. - renderer.SetForegroundColor(style.color()); - const int font_style = (style.style(BOLD) ? Font::BOLD : 0) | - (style.style(ITALIC) ? Font::ITALIC : 0); - renderer.SetFontFamilyWithStyle(family_name, font_style); - renderer.DrawPosText(&pos[style_start_glyph_index], - &glyphs[style_start_glyph_index], - glyph_index - style_start_glyph_index); - if (style.style(UNDERLINE)) - SetPangoUnderlineMetrics(desc.get(), &renderer); - renderer.DrawDecorations(style_start_x, y, x - style_start_x, - style.style(UNDERLINE), style.style(STRIKE), - style.style(DIAGONAL_STRIKE)); - style.UpdatePosition(glyph_text_index); - style_range = style.GetRange(); - style_start_glyph_index = glyph_index; - style_start_x = x; - } - // Terminates loop when the end of the range has been reached or the next - // glyph falls outside the display area. - } while (glyph_index < glyph_count && - SkScalarTruncToInt(x) < display_rect().right()); - } - - renderer.EndDiagonalStrike(); - - // Undo the temporarily applied composition underlines and selection colors. - UndoCompositionAndSelectionStyles(); -} - -GSList* RenderTextPango::GetRunContainingCaret( - const SelectionModel& caret) const { - size_t position = TextIndexToLayoutIndex(caret.caret_pos()); - LogicalCursorDirection affinity = caret.caret_affinity(); - GSList* run = current_line_->runs; - while (run) { - PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item; - Range item_range(item->offset, item->offset + item->length); - if (RangeContainsCaret(item_range, position, affinity)) - return run; - run = run->next; - } - return NULL; -} - -SelectionModel RenderTextPango::FirstSelectionModelInsideRun( - const PangoItem* item) { - size_t caret = IndexOfAdjacentGrapheme( - LayoutIndexToTextIndex(item->offset), CURSOR_FORWARD); - return SelectionModel(caret, CURSOR_BACKWARD); -} - -SelectionModel RenderTextPango::LastSelectionModelInsideRun( - const PangoItem* item) { - size_t caret = IndexOfAdjacentGrapheme( - LayoutIndexToTextIndex(item->offset + item->length), CURSOR_BACKWARD); - return SelectionModel(caret, CURSOR_FORWARD); -} - -size_t RenderTextPango::GetGlyphTextIndex(PangoLayoutRun* run, - int glyph_index) const { - return LayoutIndexToTextIndex(run->item->offset + - run->glyphs->log_clusters[glyph_index]); -} - -RenderText* RenderText::CreateNativeInstance() { - return new RenderTextPango; -} - -} // namespace gfx diff --git a/ui/gfx/render_text_pango.h b/ui/gfx/render_text_pango.h deleted file mode 100644 index 885bc57..0000000 --- a/ui/gfx/render_text_pango.h +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) 2012 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. - -#ifndef UI_GFX_RENDER_TEXT_PANGO_H_ -#define UI_GFX_RENDER_TEXT_PANGO_H_ - -#include <pango/pango.h> -#include <vector> - -#include "ui/gfx/render_text.h" - -namespace gfx { - -// RenderTextPango is the Linux implementation of RenderText using Pango. -class RenderTextPango : public RenderText { - public: - RenderTextPango(); - ~RenderTextPango() override; - - // Overridden from RenderText: - scoped_ptr<RenderText> CreateInstanceOfSameType() const override; - Size GetStringSize() override; - SelectionModel FindCursorPosition(const Point& point) override; - std::vector<FontSpan> GetFontSpansForTesting() override; - - protected: - // Overridden from RenderText: - int GetLayoutTextBaseline() override; - SelectionModel AdjacentCharSelectionModel( - const SelectionModel& selection, - VisualCursorDirection direction) override; - SelectionModel AdjacentWordSelectionModel( - const SelectionModel& selection, - VisualCursorDirection direction) override; - Range GetGlyphBounds(size_t index) override; - std::vector<Rect> GetSubstringBounds(const Range& range) override; - size_t TextIndexToLayoutIndex(size_t index) const override; - size_t LayoutIndexToTextIndex(size_t index) const override; - bool IsValidCursorIndex(size_t index) override; - void ResetLayout() override; - void EnsureLayout() override; - void DrawVisualText(Canvas* canvas) override; - - private: - friend class RenderTextTest; - FRIEND_TEST_ALL_PREFIXES(RenderTextTest, PangoAttributes); - - // Returns the run that contains the character attached to the caret in the - // given selection model. Return NULL if not found. - GSList* GetRunContainingCaret(const SelectionModel& caret) const; - - // Given a |run|, returns the SelectionModel that contains the logical first - // or last caret position inside (not at a boundary of) the run. - // The returned value represents a cursor/caret position without a selection. - SelectionModel FirstSelectionModelInsideRun(const PangoItem* run); - SelectionModel LastSelectionModelInsideRun(const PangoItem* run); - - // Setup pango attribute: foreground, background, font, strike. - void SetupPangoAttributes(PangoLayout* layout); - - // Append one pango attribute |pango_attr| into pango attribute list |attrs|. - void AppendPangoAttribute(size_t start, - size_t end, - PangoAttribute* pango_attr, - PangoAttrList* attrs); - - // Get the text index corresponding to the |run|'s |glyph_index|. - size_t GetGlyphTextIndex(PangoLayoutRun* run, int glyph_index) const; - - // Pango Layout. - PangoLayout* layout_; - // A single line layout resulting from laying out via |layout_|. - PangoLayoutLine* current_line_; - - // Information about character attributes. - PangoLogAttr* log_attrs_; - // Number of attributes in |log_attrs_|. - int num_log_attrs_; - - // The text in the |layout_|. - const char* layout_text_; - - DISALLOW_COPY_AND_ASSIGN(RenderTextPango); -}; - -} // namespace gfx - -#endif // UI_GFX_RENDER_TEXT_PANGO_H_ diff --git a/ui/gfx/render_text_unittest.cc b/ui/gfx/render_text_unittest.cc index 03425aa..f947d75 100644 --- a/ui/gfx/render_text_unittest.cc +++ b/ui/gfx/render_text_unittest.cc @@ -23,11 +23,6 @@ #if defined(OS_WIN) #include "base/win/windows_version.h" #include "ui/gfx/platform_font_win.h" -#include "ui/gfx/render_text_win.h" -#endif - -#if defined(OS_LINUX) && !defined(USE_OZONE) -#include "ui/gfx/render_text_pango.h" #endif using base::ASCIIToUTF16; @@ -43,12 +38,10 @@ namespace { const wchar_t kWeak[] = L" . "; const wchar_t kLtr[] = L"abc"; const wchar_t kRtl[] = L"\x5d0\x5d1\x5d2"; -#if !defined(OS_MACOSX) const wchar_t kLtrRtl[] = L"a" L"\x5d0\x5d1"; const wchar_t kLtrRtlLtr[] = L"a" L"\x5d1" L"b"; const wchar_t kRtlLtr[] = L"\x5d0\x5d1" L"a"; const wchar_t kRtlLtrRtl[] = L"\x5d0" L"a" L"\x5d1"; -#endif // Checks whether |range| contains |index|. This is not the same as calling // range.Contains(Range(index)), which returns true if |index| == |range.end()|. @@ -207,54 +200,6 @@ TEST_F(RenderTextTest, ApplyColorAndStyle) { #endif // OS_MACOSX } -#if defined(OS_LINUX) && !defined(USE_OZONE) -TEST_F(RenderTextTest, PangoAttributes) { - scoped_ptr<RenderText> render_text(RenderText::CreateNativeInstance()); - render_text->SetText(ASCIIToUTF16("012345678")); - - // Apply ranged BOLD/ITALIC styles and check the resulting Pango attributes. - render_text->ApplyStyle(BOLD, true, Range(2, 4)); - render_text->ApplyStyle(ITALIC, true, Range(1, 3)); - - struct { - int start; - int end; - bool bold; - bool italic; - } cases[] = { - { 0, 1, false, false }, - { 1, 2, false, true }, - { 2, 3, true, true }, - { 3, 4, true, false }, - { 4, INT_MAX, false, false }, - }; - - int start = 0, end = 0; - RenderTextPango* rt_linux = static_cast<RenderTextPango*>(render_text.get()); - rt_linux->EnsureLayout(); - PangoAttrList* attributes = pango_layout_get_attributes(rt_linux->layout_); - PangoAttrIterator* iter = pango_attr_list_get_iterator(attributes); - for (size_t i = 0; i < arraysize(cases); ++i) { - pango_attr_iterator_range(iter, &start, &end); - EXPECT_EQ(cases[i].start, start); - EXPECT_EQ(cases[i].end, end); - PangoFontDescription* font = pango_font_description_new(); - pango_attr_iterator_get_font(iter, font, NULL, NULL); - char* description_string = pango_font_description_to_string(font); - const base::string16 desc = ASCIIToUTF16(description_string); - const bool bold = desc.find(ASCIIToUTF16("Bold")) != std::string::npos; - EXPECT_EQ(cases[i].bold, bold); - const bool italic = desc.find(ASCIIToUTF16("Italic")) != std::string::npos; - EXPECT_EQ(cases[i].italic, italic); - pango_attr_iterator_next(iter); - pango_font_description_free(font); - g_free(description_string); - } - EXPECT_FALSE(pango_attr_iterator_next(iter)); - pango_attr_iterator_destroy(iter); -} -#endif - // TODO(asvitkine): Cursor movements tests disabled on Mac because RenderTextMac // does not implement this yet. http://crbug.com/131618 #if !defined(OS_MACOSX) @@ -849,8 +794,9 @@ TEST_F(RenderTextTest, MoveCursorLeftRight_ComplexScript) { #endif // TODO(ckocagil): Enable for RenderTextHarfBuzz. http://crbug.com/383265 +#if defined(OS_MACOSX) TEST_F(RenderTextTest, MoveCursorLeftRight_MeiryoUILigatures) { - scoped_ptr<RenderText> render_text(RenderText::CreateNativeInstance()); + scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); // Meiryo UI uses single-glyph ligatures for 'ff' and 'ffi', but each letter // (code point) has unique bounds, so mid-glyph cursoring should be possible. render_text->SetFontList(FontList("Meiryo UI, 12px")); @@ -862,6 +808,7 @@ TEST_F(RenderTextTest, MoveCursorLeftRight_MeiryoUILigatures) { } EXPECT_EQ(6U, render_text->cursor_position()); } +#endif // defined(OS_MACOSX) TEST_F(RenderTextTest, GraphemePositions) { // LTR 2-character grapheme, LTR abc, LTR 2-character grapheme. @@ -1304,23 +1251,6 @@ TEST_F(RenderTextTest, MoveLeftRightByWordInChineseText) { } #endif -// TODO(ckocagil): Remove when RenderTextWin goes away. -#if defined(OS_WIN) -TEST_F(RenderTextTest, Win_LogicalClusters) { - scoped_ptr<RenderTextWin> render_text( - static_cast<RenderTextWin*>(RenderText::CreateNativeInstance())); - - const base::string16 test_string = - WideToUTF16(L"\x0930\x0930\x0930\x0930\x0930"); - render_text->SetText(test_string); - render_text->EnsureLayout(); - ASSERT_EQ(1U, render_text->runs_.size()); - WORD* logical_clusters = render_text->runs_[0]->logical_clusters.get(); - for (size_t i = 0; i < test_string.length(); ++i) - EXPECT_EQ(i, logical_clusters[i]); -} -#endif // defined(OS_WIN) - TEST_F(RenderTextTest, StringSizeSanity) { scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); render_text->SetText(UTF8ToUTF16("Hello World")); @@ -1933,15 +1863,13 @@ TEST_F(RenderTextTest, SelectionKeepsLigatures) { } } -#if defined(OS_WIN) // TODO(ckocagil): Enable for RenderTextHarfBuzz after implementing multiline. // Ensure strings wrap onto multiple lines for a small available width. -TEST_F(RenderTextTest, Multiline_MinWidth) { +TEST_F(RenderTextTest, DISABLED_Multiline_MinWidth) { const wchar_t* kTestStrings[] = { kWeak, kLtr, kLtrRtl, kLtrRtlLtr, kRtl, kRtlLtr, kRtlLtrRtl }; - scoped_ptr<RenderTextWin> render_text( - static_cast<RenderTextWin*>(RenderText::CreateNativeInstance())); + scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); render_text->SetDisplayRect(Rect(1, 1000)); render_text->SetMultiline(true); Canvas canvas; @@ -1956,7 +1884,7 @@ TEST_F(RenderTextTest, Multiline_MinWidth) { // TODO(ckocagil): Enable for RenderTextHarfBuzz after implementing multiline. // Ensure strings wrap onto multiple lines for a normal available width. -TEST_F(RenderTextTest, Multiline_NormalWidth) { +TEST_F(RenderTextTest, DISABLED_Multiline_NormalWidth) { const struct { const wchar_t* const text; const Range first_line_char_range; @@ -1968,8 +1896,7 @@ TEST_F(RenderTextTest, Multiline_NormalWidth) { Range(4, 10), Range(0, 4) } }; - scoped_ptr<RenderTextWin> render_text( - static_cast<RenderTextWin*>(RenderText::CreateNativeInstance())); + scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); render_text->SetDisplayRect(Rect(50, 1000)); render_text->SetMultiline(true); Canvas canvas; @@ -1991,12 +1918,11 @@ TEST_F(RenderTextTest, Multiline_NormalWidth) { // TODO(ckocagil): Enable for RenderTextHarfBuzz after implementing multiline. // Ensure strings don't wrap onto multiple lines for a sufficient available // width. -TEST_F(RenderTextTest, Multiline_SufficientWidth) { +TEST_F(RenderTextTest, DISABLED_Multiline_SufficientWidth) { const wchar_t* kTestStrings[] = { L"", L" ", L".", L" . ", L"abc", L"a b c", L"\x62E\x628\x632", L"\x62E \x628 \x632" }; - scoped_ptr<RenderTextWin> render_text( - static_cast<RenderTextWin*>(RenderText::CreateNativeInstance())); + scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); render_text->SetDisplayRect(Rect(30, 1000)); render_text->SetMultiline(true); Canvas canvas; @@ -2010,7 +1936,7 @@ TEST_F(RenderTextTest, Multiline_SufficientWidth) { } // TODO(ckocagil): Enable for RenderTextHarfBuzz after implementing multiline. -TEST_F(RenderTextTest, Multiline_Newline) { +TEST_F(RenderTextTest, DISABLED_Multiline_Newline) { const struct { const wchar_t* const text; // Ranges of the characters on each line preceding the newline. @@ -2022,8 +1948,7 @@ TEST_F(RenderTextTest, Multiline_Newline) { { L"\n" , Range::InvalidRange(), Range::InvalidRange() } }; - scoped_ptr<RenderTextWin> render_text( - static_cast<RenderTextWin*>(RenderText::CreateNativeInstance())); + scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); render_text->SetDisplayRect(Rect(200, 1000)); render_text->SetMultiline(true); Canvas canvas; @@ -2056,28 +1981,6 @@ TEST_F(RenderTextTest, Multiline_Newline) { } } -// TODO(ckocagil): Remove when RenderTextWin goes away. -TEST_F(RenderTextTest, BreakRunsByUnicodeBlocks) { - scoped_ptr<RenderTextWin> render_text( - static_cast<RenderTextWin*>(RenderText::CreateNativeInstance())); - - // The '\x25B6' "play character" should break runs. http://crbug.com/278913 - render_text->SetText(WideToUTF16(L"x\x25B6y")); - render_text->EnsureLayout(); - ASSERT_EQ(3U, render_text->runs_.size()); - EXPECT_EQ(Range(0, 1), render_text->runs_[0]->range); - EXPECT_EQ(Range(1, 2), render_text->runs_[1]->range); - EXPECT_EQ(Range(2, 3), render_text->runs_[2]->range); - - render_text->SetText(WideToUTF16(L"x \x25B6 y")); - render_text->EnsureLayout(); - ASSERT_EQ(3U, render_text->runs_.size()); - EXPECT_EQ(Range(0, 2), render_text->runs_[0]->range); - EXPECT_EQ(Range(2, 3), render_text->runs_[1]->range); - EXPECT_EQ(Range(3, 5), render_text->runs_[2]->range); -} -#endif // defined(OS_WIN) - // Test TextRunHarfBuzz's cluster finding logic. TEST_F(RenderTextTest, HarfBuzz_Clusters) { struct { @@ -2268,29 +2171,10 @@ TEST_F(RenderTextTest, HarfBuzz_BreakRunsByEmoji) { EXPECT_EQ(Range(4, 5), render_text.runs_[3]->range); } -// Disabled on Mac because RenderTextMac doesn't implement GetGlyphBounds. -#if !defined(OS_MACOSX) TEST_F(RenderTextTest, GlyphBounds) { const wchar_t* kTestStrings[] = { L"asdf 1234 qwer", L"\x0647\x0654", L"\x0645\x0631\x062D\x0628\x0627" }; - scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); - - for (size_t i = 0; i < arraysize(kTestStrings); ++i) { - render_text->SetText(WideToUTF16(kTestStrings[i])); - render_text->EnsureLayout(); - - for (size_t j = 0; j < render_text->text().length(); ++j) - EXPECT_FALSE(render_text->GetGlyphBounds(j).is_empty()); - } -} -#endif - -// Remove this after making RTHB default in favor of RenderTextTest.GlyphBounds. -TEST_F(RenderTextTest, HarfBuzz_GlyphBounds) { - const wchar_t* kTestStrings[] = { - L"asdf 1234 qwer", L"\x0647\x0654", L"\x0645\x0631\x062D\x0628\x0627" - }; scoped_ptr<RenderText> render_text(new RenderTextHarfBuzz); for (size_t i = 0; i < arraysize(kTestStrings); ++i) { diff --git a/ui/gfx/render_text_win.cc b/ui/gfx/render_text_win.cc deleted file mode 100644 index f192599..0000000 --- a/ui/gfx/render_text_win.cc +++ /dev/null @@ -1,1261 +0,0 @@ -// Copyright (c) 2012 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 "ui/gfx/render_text_win.h" - -#include <algorithm> - -#include "base/i18n/break_iterator.h" -#include "base/i18n/char_iterator.h" -#include "base/i18n/rtl.h" -#include "base/logging.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "base/win/windows_version.h" -#include "third_party/icu/source/common/unicode/uchar.h" -#include "ui/gfx/canvas.h" -#include "ui/gfx/font_fallback_win.h" -#include "ui/gfx/font_render_params.h" -#include "ui/gfx/geometry/size_conversions.h" -#include "ui/gfx/platform_font_win.h" -#include "ui/gfx/utf16_indexing.h" - -namespace gfx { - -namespace { - -// The maximum length of text supported for Uniscribe layout and display. -// This empirically chosen value should prevent major performance degradations. -// TODO(msw): Support longer text, partial layout/painting, etc. -const size_t kMaxUniscribeTextLength = 10000; - -// The initial guess and maximum supported number of runs; arbitrary values. -// TODO(msw): Support more runs, determine a better initial guess, etc. -const int kGuessRuns = 100; -const size_t kMaxRuns = 10000; - -// The maximum number of glyphs per run; ScriptShape fails on larger values. -const size_t kMaxGlyphs = 65535; - -// Changes |font| to have the specified |font_size| (or |font_height| on Windows -// XP) and |font_style| if it is not the case already. Only considers bold and -// italic styles, since the underlined style has no effect on glyph shaping. -void DeriveFontIfNecessary(int font_size, - int font_height, - int font_style, - Font* font) { - const int kStyleMask = (Font::BOLD | Font::ITALIC); - const int target_style = (font_style & kStyleMask); - - // On Windows XP, the font must be resized using |font_height| instead of - // |font_size| to match GDI behavior. - if (base::win::GetVersion() < base::win::VERSION_VISTA) { - PlatformFontWin* platform_font = - static_cast<PlatformFontWin*>(font->platform_font()); - *font = platform_font->DeriveFontWithHeight(font_height, target_style); - return; - } - - const int current_style = (font->GetStyle() & kStyleMask); - const int current_size = font->GetFontSize(); - if (current_style != target_style || current_size != font_size) - *font = font->Derive(font_size - current_size, target_style); -} - -// Returns true if |c| is a Unicode BiDi control character. -bool IsUnicodeBidiControlCharacter(base::char16 c) { - return c == base::i18n::kRightToLeftMark || - c == base::i18n::kLeftToRightMark || - c == base::i18n::kLeftToRightEmbeddingMark || - c == base::i18n::kRightToLeftEmbeddingMark || - c == base::i18n::kPopDirectionalFormatting || - c == base::i18n::kLeftToRightOverride || - c == base::i18n::kRightToLeftOverride; -} - -// Returns the corresponding glyph range of the given character range. -// |range| is in text-space (0 corresponds to |GetLayoutText()[0]|). -// Returned value is in run-space (0 corresponds to the first glyph in the run). -Range CharRangeToGlyphRange(const internal::TextRun& run, - const Range& range) { - DCHECK(run.range.Contains(range)); - DCHECK(!range.is_reversed()); - DCHECK(!range.is_empty()); - const Range run_range(range.start() - run.range.start(), - range.end() - run.range.start()); - Range result; - if (run.script_analysis.fRTL) { - result = Range(run.logical_clusters[run_range.end() - 1], - run_range.start() > 0 ? run.logical_clusters[run_range.start() - 1] - : run.glyph_count); - } else { - result = Range(run.logical_clusters[run_range.start()], - run_range.end() < run.range.length() ? - run.logical_clusters[run_range.end()] : run.glyph_count); - } - DCHECK(!result.is_reversed()); - DCHECK(Range(0, run.glyph_count).Contains(result)); - return result; -} - -// Starting from |start_char|, finds a suitable line break position at or before -// |available_width| using word break info from |breaks|. If |empty_line| is -// true, this function will not roll back to |start_char| and |*next_char| will -// be greater than |start_char| (to avoid constructing empty lines). Returns -// whether to skip the line before |*next_char|. -// TODO(ckocagil): Do not break ligatures and diacritics. -// TextRun::logical_clusters might help. -// TODO(ckocagil): We might have to reshape after breaking at ligatures. -// See whether resolving the TODO above resolves this too. -// TODO(ckocagil): Do not reserve width for whitespace at the end of lines. -bool BreakRunAtWidth(const wchar_t* text, - const internal::TextRun& run, - const BreakList<size_t>& breaks, - size_t start_char, - int available_width, - bool empty_line, - int* width, - size_t* next_char) { - DCHECK(run.range.Contains(Range(start_char, start_char + 1))); - BreakList<size_t>::const_iterator word = breaks.GetBreak(start_char); - BreakList<size_t>::const_iterator next_word = word + 1; - // Width from |std::max(word->first, start_char)| to the current character. - int word_width = 0; - *width = 0; - - for (size_t i = start_char; i < run.range.end(); ++i) { - if (U16_IS_SINGLE(text[i]) && text[i] == L'\n') { - *next_char = i + 1; - return true; - } - - // |word| holds the word boundary at or before |i|, and |next_word| holds - // the word boundary right after |i|. Advance both |word| and |next_word| - // when |i| reaches |next_word|. - if (next_word != breaks.breaks().end() && i >= next_word->first) { - word = next_word++; - word_width = 0; - } - - Range glyph_range = CharRangeToGlyphRange(run, Range(i, i + 1)); - int char_width = 0; - for (size_t j = glyph_range.start(); j < glyph_range.end(); ++j) - char_width += run.advance_widths[j]; - - *width += char_width; - word_width += char_width; - - if (*width > available_width) { - if (!empty_line || word_width < *width) { - // Roll back one word. - *width -= word_width; - *next_char = std::max(word->first, start_char); - } else if (char_width < *width) { - // Roll back one character. - *width -= char_width; - *next_char = i; - } else { - // Continue from the next character. - *next_char = i + 1; - } - - return true; - } - } - - *next_char = run.range.end(); - return false; -} - -// For segments in the same run, checks the continuity and order of |x_range| -// and |char_range| fields. -void CheckLineIntegrity(const std::vector<internal::Line>& lines, - const ScopedVector<internal::TextRun>& runs) { - size_t previous_segment_line = 0; - const internal::LineSegment* previous_segment = NULL; - - for (size_t i = 0; i < lines.size(); ++i) { - for (size_t j = 0; j < lines[i].segments.size(); ++j) { - const internal::LineSegment* segment = &lines[i].segments[j]; - internal::TextRun* run = runs[segment->run]; - - if (!previous_segment) { - previous_segment = segment; - } else if (runs[previous_segment->run] != run) { - previous_segment = NULL; - } else { - DCHECK_EQ(previous_segment->char_range.end(), - segment->char_range.start()); - if (!run->script_analysis.fRTL) { - DCHECK_EQ(previous_segment->x_range.end(), segment->x_range.start()); - } else { - DCHECK_EQ(segment->x_range.end(), previous_segment->x_range.start()); - } - - previous_segment = segment; - previous_segment_line = i; - } - } - } -} - -// Returns true if characters of |block_code| may trigger font fallback. -bool IsUnusualBlockCode(const UBlockCode block_code) { - return block_code == UBLOCK_GEOMETRIC_SHAPES || - block_code == UBLOCK_MISCELLANEOUS_SYMBOLS; -} - -// Returns the index of the first unusual character after a usual character or -// vice versa. Unusual characters are defined by |IsUnusualBlockCode|. -size_t FindUnusualCharacter(const base::string16& text, - size_t run_start, - size_t run_break) { - const int32 run_length = static_cast<int32>(run_break - run_start); - base::i18n::UTF16CharIterator iter(text.c_str() + run_start, - run_length); - const UBlockCode first_block_code = ublock_getCode(iter.get()); - const bool first_block_unusual = IsUnusualBlockCode(first_block_code); - while (iter.Advance() && iter.array_pos() < run_length) { - const UBlockCode current_block_code = ublock_getCode(iter.get()); - if (current_block_code != first_block_code && - (first_block_unusual || IsUnusualBlockCode(current_block_code))) { - return run_start + iter.array_pos(); - } - } - return run_break; -} - -} // namespace - -namespace internal { - -TextRun::TextRun() - : font_style(0), - strike(false), - diagonal_strike(false), - underline(false), - width(0), - preceding_run_widths(0), - glyph_count(0), - script_cache(NULL) { - memset(&script_analysis, 0, sizeof(script_analysis)); - memset(&abc_widths, 0, sizeof(abc_widths)); -} - -TextRun::~TextRun() { - ScriptFreeCache(&script_cache); -} - -// Returns the X coordinate of the leading or |trailing| edge of the glyph -// starting at |index|, relative to the left of the text (not the view). -int GetGlyphXBoundary(const internal::TextRun* run, - size_t index, - bool trailing) { - DCHECK_GE(index, run->range.start()); - DCHECK_LT(index, run->range.end() + (trailing ? 0 : 1)); - int x = 0; - HRESULT hr = ScriptCPtoX( - index - run->range.start(), - trailing, - run->range.length(), - run->glyph_count, - run->logical_clusters.get(), - run->visible_attributes.get(), - run->advance_widths.get(), - &run->script_analysis, - &x); - DCHECK(SUCCEEDED(hr)); - return run->preceding_run_widths + x; -} - -// Internal class to generate Line structures. If |multiline| is true, the text -// is broken into lines at |words| boundaries such that each line is no longer -// than |max_width|. If |multiline| is false, only outputs a single Line from -// the given runs. |min_baseline| and |min_height| are the minimum baseline and -// height for each line. -// TODO(ckocagil): Expose the interface of this class in the header and test -// this class directly. -class LineBreaker { - public: - LineBreaker(int max_width, - int min_baseline, - int min_height, - bool multiline, - const wchar_t* text, - const BreakList<size_t>* words, - const ScopedVector<TextRun>& runs) - : max_width_(max_width), - min_baseline_(min_baseline), - min_height_(min_height), - multiline_(multiline), - text_(text), - words_(words), - runs_(runs), - text_x_(0), - line_x_(0), - line_ascent_(0), - line_descent_(0) { - AdvanceLine(); - } - - // Breaks the run at given |run_index| into Line structs. - void AddRun(int run_index) { - const TextRun* run = runs_[run_index]; - bool run_fits = !multiline_; - if (multiline_ && line_x_ + run->width <= max_width_) { - DCHECK(!run->range.is_empty()); - const wchar_t first_char = text_[run->range.start()]; - // Uniscribe always puts newline characters in their own runs. - if (!U16_IS_SINGLE(first_char) || first_char != L'\n') - run_fits = true; - } - - if (!run_fits) - BreakRun(run_index); - else - AddSegment(run_index, run->range, run->width); - } - - // Finishes line breaking and outputs the results. Can be called at most once. - void Finalize(std::vector<Line>* lines, Size* size) { - DCHECK(!lines_.empty()); - // Add an empty line to finish the line size calculation and remove it. - AdvanceLine(); - lines_.pop_back(); - *size = total_size_; - lines->swap(lines_); - } - - private: - // A (line index, segment index) pair that specifies a segment in |lines_|. - typedef std::pair<size_t, size_t> SegmentHandle; - - LineSegment* SegmentFromHandle(const SegmentHandle& handle) { - return &lines_[handle.first].segments[handle.second]; - } - - // Breaks a run into segments that fit in the last line in |lines_| and adds - // them. Adds a new Line to the back of |lines_| whenever a new segment can't - // be added without the Line's width exceeding |max_width_|. - void BreakRun(int run_index) { - DCHECK(words_); - const TextRun* const run = runs_[run_index]; - int width = 0; - size_t next_char = run->range.start(); - - // Break the run until it fits the current line. - while (next_char < run->range.end()) { - const size_t current_char = next_char; - const bool skip_line = BreakRunAtWidth(text_, *run, *words_, current_char, - max_width_ - line_x_, line_x_ == 0, &width, &next_char); - AddSegment(run_index, Range(current_char, next_char), width); - if (skip_line) - AdvanceLine(); - } - } - - // RTL runs are broken in logical order but displayed in visual order. To find - // the text-space coordinate (where it would fall in a single-line text) - // |x_range| of RTL segments, segment widths are applied in reverse order. - // e.g. {[5, 10], [10, 40]} will become {[35, 40], [5, 35]}. - void UpdateRTLSegmentRanges() { - if (rtl_segments_.empty()) - return; - int x = SegmentFromHandle(rtl_segments_[0])->x_range.start(); - for (size_t i = rtl_segments_.size(); i > 0; --i) { - LineSegment* segment = SegmentFromHandle(rtl_segments_[i - 1]); - const size_t segment_width = segment->x_range.length(); - segment->x_range = Range(x, x + segment_width); - x += segment_width; - } - rtl_segments_.clear(); - } - - // Finishes the size calculations of the last Line in |lines_|. Adds a new - // Line to the back of |lines_|. - void AdvanceLine() { - if (!lines_.empty()) { - Line* line = &lines_.back(); - // TODO(ckocagil): Determine optimal multiline height behavior. - if (line_ascent_ + line_descent_ == 0) { - line_ascent_ = min_baseline_; - line_descent_ = min_height_ - min_baseline_; - } - // Set the single-line mode Line's metrics to be at least - // |RenderText::font_list()| to not break the current single-line code. - line_ascent_ = std::max(line_ascent_, min_baseline_); - line_descent_ = std::max(line_descent_, min_height_ - min_baseline_); - - line->baseline = line_ascent_; - line->size.set_height(line_ascent_ + line_descent_); - line->preceding_heights = total_size_.height(); - const Size line_size(ToCeiledSize(line->size)); - total_size_.set_height(total_size_.height() + line_size.height()); - total_size_.set_width(std::max(total_size_.width(), line_size.width())); - } - line_x_ = 0; - line_ascent_ = 0; - line_descent_ = 0; - lines_.push_back(Line()); - } - - // Adds a new segment with the given properties to |lines_.back()|. - void AddSegment(int run_index, Range char_range, int width) { - if (char_range.is_empty()) { - DCHECK_EQ(width, 0); - return; - } - const TextRun* run = runs_[run_index]; - line_ascent_ = std::max(line_ascent_, run->font.GetBaseline()); - line_descent_ = std::max(line_descent_, - run->font.GetHeight() - run->font.GetBaseline()); - - LineSegment segment; - segment.run = run_index; - segment.char_range = char_range; - segment.x_range = Range(text_x_, text_x_ + width); - - Line* line = &lines_.back(); - line->segments.push_back(segment); - line->size.set_width(line->size.width() + segment.x_range.length()); - if (run->script_analysis.fRTL) { - rtl_segments_.push_back(SegmentHandle(lines_.size() - 1, - line->segments.size() - 1)); - // If this is the last segment of an RTL run, reprocess the text-space x - // ranges of all segments from the run. - if (char_range.end() == run->range.end()) - UpdateRTLSegmentRanges(); - } - text_x_ += width; - line_x_ += width; - } - - const int max_width_; - const int min_baseline_; - const int min_height_; - const bool multiline_; - const wchar_t* text_; - const BreakList<size_t>* const words_; - const ScopedVector<TextRun>& runs_; - - // Stores the resulting lines. - std::vector<Line> lines_; - - // Text space and line space x coordinates of the next segment to be added. - int text_x_; - int line_x_; - - // Size of the multiline text, not including the currently processed line. - Size total_size_; - - // Ascent and descent values of the current line, |lines_.back()|. - int line_ascent_; - int line_descent_; - - // The current RTL run segments, to be applied by |UpdateRTLSegmentRanges()|. - std::vector<SegmentHandle> rtl_segments_; - - DISALLOW_COPY_AND_ASSIGN(LineBreaker); -}; - -} // namespace internal - -// static -HDC RenderTextWin::cached_hdc_ = NULL; - -// static -std::map<std::string, Font> RenderTextWin::successful_substitute_fonts_; - -RenderTextWin::RenderTextWin() : RenderText(), needs_layout_(false) { - set_truncate_length(kMaxUniscribeTextLength); - memset(&script_control_, 0, sizeof(script_control_)); - memset(&script_state_, 0, sizeof(script_state_)); - MoveCursorTo(EdgeSelectionModel(CURSOR_LEFT)); -} - -RenderTextWin::~RenderTextWin() {} - -scoped_ptr<RenderText> RenderTextWin::CreateInstanceOfSameType() const { - return scoped_ptr<RenderTextWin>(new RenderTextWin); -} - -Size RenderTextWin::GetStringSize() { - EnsureLayout(); - return multiline_string_size_; -} - -SelectionModel RenderTextWin::FindCursorPosition(const Point& point) { - if (text().empty()) - return SelectionModel(); - - EnsureLayout(); - // Find the run that contains the point and adjust the argument location. - int x = ToTextPoint(point).x(); - size_t run_index = GetRunContainingXCoord(x); - if (run_index >= runs_.size()) - return EdgeSelectionModel((x < 0) ? CURSOR_LEFT : CURSOR_RIGHT); - internal::TextRun* run = runs_[run_index]; - - int position = 0, trailing = 0; - HRESULT hr = ScriptXtoCP(x - run->preceding_run_widths, - run->range.length(), - run->glyph_count, - run->logical_clusters.get(), - run->visible_attributes.get(), - run->advance_widths.get(), - &(run->script_analysis), - &position, - &trailing); - DCHECK(SUCCEEDED(hr)); - DCHECK_GE(trailing, 0); - position += run->range.start(); - const size_t cursor = LayoutIndexToTextIndex(position + trailing); - DCHECK_LE(cursor, text().length()); - return SelectionModel(cursor, trailing ? CURSOR_BACKWARD : CURSOR_FORWARD); -} - -std::vector<RenderText::FontSpan> RenderTextWin::GetFontSpansForTesting() { - EnsureLayout(); - - std::vector<RenderText::FontSpan> spans; - for (size_t i = 0; i < runs_.size(); ++i) { - spans.push_back(RenderText::FontSpan(runs_[i]->font, - Range(LayoutIndexToTextIndex(runs_[i]->range.start()), - LayoutIndexToTextIndex(runs_[i]->range.end())))); - } - - return spans; -} - -int RenderTextWin::GetLayoutTextBaseline() { - EnsureLayout(); - return lines()[0].baseline; -} - -SelectionModel RenderTextWin::AdjacentCharSelectionModel( - const SelectionModel& selection, - VisualCursorDirection direction) { - DCHECK(!needs_layout_); - internal::TextRun* run; - size_t run_index = GetRunContainingCaret(selection); - if (run_index >= runs_.size()) { - // The cursor is not in any run: we're at the visual and logical edge. - SelectionModel edge = EdgeSelectionModel(direction); - if (edge.caret_pos() == selection.caret_pos()) - return edge; - int visual_index = (direction == CURSOR_RIGHT) ? 0 : runs_.size() - 1; - run = runs_[visual_to_logical_[visual_index]]; - } else { - // If the cursor is moving within the current run, just move it by one - // grapheme in the appropriate direction. - run = runs_[run_index]; - size_t caret = selection.caret_pos(); - bool forward_motion = - run->script_analysis.fRTL == (direction == CURSOR_LEFT); - if (forward_motion) { - if (caret < LayoutIndexToTextIndex(run->range.end())) { - caret = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD); - return SelectionModel(caret, CURSOR_BACKWARD); - } - } else { - if (caret > LayoutIndexToTextIndex(run->range.start())) { - caret = IndexOfAdjacentGrapheme(caret, CURSOR_BACKWARD); - return SelectionModel(caret, CURSOR_FORWARD); - } - } - // The cursor is at the edge of a run; move to the visually adjacent run. - int visual_index = logical_to_visual_[run_index]; - visual_index += (direction == CURSOR_LEFT) ? -1 : 1; - if (visual_index < 0 || visual_index >= static_cast<int>(runs_.size())) - return EdgeSelectionModel(direction); - run = runs_[visual_to_logical_[visual_index]]; - } - bool forward_motion = run->script_analysis.fRTL == (direction == CURSOR_LEFT); - return forward_motion ? FirstSelectionModelInsideRun(run) : - LastSelectionModelInsideRun(run); -} - -// TODO(msw): Implement word breaking for Windows. -SelectionModel RenderTextWin::AdjacentWordSelectionModel( - const SelectionModel& selection, - VisualCursorDirection direction) { - if (obscured()) - return EdgeSelectionModel(direction); - - base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); - bool success = iter.Init(); - DCHECK(success); - if (!success) - return selection; - - size_t pos; - if (direction == CURSOR_RIGHT) { - pos = std::min(selection.caret_pos() + 1, text().length()); - while (iter.Advance()) { - pos = iter.pos(); - if (iter.IsWord() && pos > selection.caret_pos()) - break; - } - } else { // direction == CURSOR_LEFT - // Notes: We always iterate words from the beginning. - // This is probably fast enough for our usage, but we may - // want to modify WordIterator so that it can start from the - // middle of string and advance backwards. - pos = std::max<int>(selection.caret_pos() - 1, 0); - while (iter.Advance()) { - if (iter.IsWord()) { - size_t begin = iter.pos() - iter.GetString().length(); - if (begin == selection.caret_pos()) { - // The cursor is at the beginning of a word. - // Move to previous word. - break; - } else if (iter.pos() >= selection.caret_pos()) { - // The cursor is in the middle or at the end of a word. - // Move to the top of current word. - pos = begin; - break; - } else { - pos = iter.pos() - iter.GetString().length(); - } - } - } - } - return SelectionModel(pos, CURSOR_FORWARD); -} - -Range RenderTextWin::GetGlyphBounds(size_t index) { - EnsureLayout(); - const size_t run_index = - GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD)); - // Return edge bounds if the index is invalid or beyond the layout text size. - if (run_index >= runs_.size()) - return Range(string_width_); - internal::TextRun* run = runs_[run_index]; - const size_t layout_index = TextIndexToLayoutIndex(index); - return Range(GetGlyphXBoundary(run, layout_index, false), - GetGlyphXBoundary(run, layout_index, true)); -} - -std::vector<Rect> RenderTextWin::GetSubstringBounds(const Range& range) { - DCHECK(!needs_layout_); - DCHECK(Range(0, text().length()).Contains(range)); - Range layout_range(TextIndexToLayoutIndex(range.start()), - TextIndexToLayoutIndex(range.end())); - DCHECK(Range(0, GetLayoutText().length()).Contains(layout_range)); - - std::vector<Rect> rects; - if (layout_range.is_empty()) - return rects; - std::vector<Range> bounds; - - // Add a Range for each run/selection intersection. - // TODO(msw): The bounds should probably not always be leading the range ends. - for (size_t i = 0; i < runs_.size(); ++i) { - const internal::TextRun* run = runs_[visual_to_logical_[i]]; - Range intersection = run->range.Intersect(layout_range); - if (intersection.IsValid()) { - DCHECK(!intersection.is_reversed()); - Range range_x(GetGlyphXBoundary(run, intersection.start(), false), - GetGlyphXBoundary(run, intersection.end(), false)); - if (range_x.is_empty()) - continue; - range_x = Range(range_x.GetMin(), range_x.GetMax()); - // Union this with the last range if they're adjacent. - DCHECK(bounds.empty() || bounds.back().GetMax() <= range_x.GetMin()); - if (!bounds.empty() && bounds.back().GetMax() == range_x.GetMin()) { - range_x = Range(bounds.back().GetMin(), range_x.GetMax()); - bounds.pop_back(); - } - bounds.push_back(range_x); - } - } - for (size_t i = 0; i < bounds.size(); ++i) { - std::vector<Rect> current_rects = TextBoundsToViewBounds(bounds[i]); - rects.insert(rects.end(), current_rects.begin(), current_rects.end()); - } - return rects; -} - -size_t RenderTextWin::TextIndexToLayoutIndex(size_t index) const { - DCHECK_LE(index, text().length()); - ptrdiff_t i = obscured() ? UTF16IndexToOffset(text(), 0, index) : index; - CHECK_GE(i, 0); - // Clamp layout indices to the length of the text actually used for layout. - return std::min<size_t>(GetLayoutText().length(), i); -} - -size_t RenderTextWin::LayoutIndexToTextIndex(size_t index) const { - if (!obscured()) - return index; - - DCHECK_LE(index, GetLayoutText().length()); - const size_t text_index = UTF16OffsetToIndex(text(), 0, index); - DCHECK_LE(text_index, text().length()); - return text_index; -} - -bool RenderTextWin::IsValidCursorIndex(size_t index) { - if (index == 0 || index == text().length()) - return true; - if (!IsValidLogicalIndex(index)) - return false; - EnsureLayout(); - // Disallow indices amid multi-character graphemes by checking glyph bounds. - // These characters are not surrogate-pairs, but may yield a single glyph: - // \x0915\x093f - (ki) - one of many Devanagari biconsonantal conjuncts. - // \x0e08\x0e33 - (cho chan + sara am) - a Thai consonant and vowel pair. - return GetGlyphBounds(index) != GetGlyphBounds(index - 1); -} - -void RenderTextWin::ResetLayout() { - // Layout is performed lazily as needed for drawing/metrics. - needs_layout_ = true; -} - -void RenderTextWin::EnsureLayout() { - if (needs_layout_) { - // TODO(msw): Skip complex processing if ScriptIsComplex returns false. - ItemizeLogicalText(); - if (!runs_.empty()) - LayoutVisualText(); - needs_layout_ = false; - std::vector<internal::Line> lines; - set_lines(&lines); - } - - // Compute lines if they're not valid. This is separate from the layout steps - // above to avoid text layout and shaping when we resize |display_rect_|. - if (lines().empty()) { - DCHECK(!needs_layout_); - std::vector<internal::Line> lines; - internal::LineBreaker line_breaker(display_rect().width() - 1, - font_list().GetBaseline(), - font_list().GetHeight(), multiline(), - GetLayoutText().c_str(), - multiline() ? &GetLineBreaks() : NULL, - runs_); - for (size_t i = 0; i < runs_.size(); ++i) - line_breaker.AddRun(visual_to_logical_[i]); - line_breaker.Finalize(&lines, &multiline_string_size_); - DCHECK(!lines.empty()); -#ifndef NDEBUG - CheckLineIntegrity(lines, runs_); -#endif - set_lines(&lines); - } -} - -void RenderTextWin::DrawVisualText(Canvas* canvas) { - DCHECK(!needs_layout_); - DCHECK(!lines().empty()); - - std::vector<SkPoint> pos; - - internal::SkiaTextRenderer renderer(canvas); - ApplyFadeEffects(&renderer); - ApplyTextShadows(&renderer); - - renderer.SetFontRenderParams( - font_list().GetPrimaryFont().GetFontRenderParams(), - background_is_transparent()); - - ApplyCompositionAndSelectionStyles(); - - for (size_t i = 0; i < lines().size(); ++i) { - const internal::Line& line = lines()[i]; - const Vector2d line_offset = GetLineOffset(i); - - // Skip painting empty lines or lines outside the display rect area. - if (!display_rect().Intersects(Rect(PointAtOffsetFromOrigin(line_offset), - ToCeiledSize(line.size)))) - continue; - - const Vector2d text_offset = line_offset + Vector2d(0, line.baseline); - int preceding_segment_widths = 0; - - for (size_t j = 0; j < line.segments.size(); ++j) { - const internal::LineSegment* segment = &line.segments[j]; - const int segment_width = segment->x_range.length(); - const internal::TextRun* run = runs_[segment->run]; - DCHECK(!segment->char_range.is_empty()); - DCHECK(run->range.Contains(segment->char_range)); - Range glyph_range = CharRangeToGlyphRange(*run, segment->char_range); - DCHECK(!glyph_range.is_empty()); - // Skip painting segments outside the display rect area. - if (!multiline()) { - const Rect segment_bounds(PointAtOffsetFromOrigin(line_offset) + - Vector2d(preceding_segment_widths, 0), - Size(segment_width, line.size.height())); - if (!display_rect().Intersects(segment_bounds)) { - preceding_segment_widths += segment_width; - continue; - } - } - - // |pos| contains the positions of glyphs. An extra terminal |pos| entry - // is added to simplify width calculations. - int segment_x = preceding_segment_widths; - pos.resize(glyph_range.length() + 1); - for (size_t k = glyph_range.start(); k < glyph_range.end(); ++k) { - pos[k - glyph_range.start()].set( - SkIntToScalar(text_offset.x() + run->offsets[k].du + segment_x), - SkIntToScalar(text_offset.y() - run->offsets[k].dv)); - segment_x += run->advance_widths[k]; - } - pos.back().set(SkIntToScalar(text_offset.x() + segment_x), - SkIntToScalar(text_offset.y())); - - renderer.SetTextSize(SkIntToScalar(run->font.GetFontSize())); - renderer.SetFontFamilyWithStyle(run->font.GetFontName(), run->font_style); - - for (BreakList<SkColor>::const_iterator it = - colors().GetBreak(segment->char_range.start()); - it != colors().breaks().end() && - it->first < segment->char_range.end(); - ++it) { - const Range intersection = - colors().GetRange(it).Intersect(segment->char_range); - const Range colored_glyphs = CharRangeToGlyphRange(*run, intersection); - // The range may be empty if a portion of a multi-character grapheme is - // selected, yielding two colors for a single glyph. For now, this just - // paints the glyph with a single style, but it should paint it twice, - // clipped according to selection bounds. See http://crbug.com/366786 - if (colored_glyphs.is_empty()) - continue; - DCHECK(glyph_range.Contains(colored_glyphs)); - const SkPoint& start_pos = - pos[colored_glyphs.start() - glyph_range.start()]; - const SkPoint& end_pos = - pos[colored_glyphs.end() - glyph_range.start()]; - - renderer.SetForegroundColor(it->second); - renderer.DrawPosText(&start_pos, &run->glyphs[colored_glyphs.start()], - colored_glyphs.length()); - int start_x = SkScalarRoundToInt(start_pos.x()); - renderer.DrawDecorations( - start_x, text_offset.y(), SkScalarRoundToInt(end_pos.x()) - start_x, - run->underline, run->strike, run->diagonal_strike); - } - - preceding_segment_widths += segment_width; - } - - renderer.EndDiagonalStrike(); - } - - UndoCompositionAndSelectionStyles(); -} - -void RenderTextWin::ItemizeLogicalText() { - runs_.clear(); - string_width_ = 0; - multiline_string_size_ = Size(); - - // Set Uniscribe's base text direction. - script_state_.uBidiLevel = - (GetTextDirection() == base::i18n::RIGHT_TO_LEFT) ? 1 : 0; - - const base::string16& layout_text = GetLayoutText(); - if (layout_text.empty()) - return; - - HRESULT hr = E_OUTOFMEMORY; - int script_items_count = 0; - std::vector<SCRIPT_ITEM> script_items; - const size_t layout_text_length = layout_text.length(); - // Ensure that |kMaxRuns| is attempted and the loop terminates afterward. - for (size_t runs = kGuessRuns; hr == E_OUTOFMEMORY && runs <= kMaxRuns; - runs = std::max(runs + 1, std::min(runs * 2, kMaxRuns))) { - // Derive the array of Uniscribe script items from the logical text. - // ScriptItemize always adds a terminal array item so that the length of - // the last item can be derived from the terminal SCRIPT_ITEM::iCharPos. - script_items.resize(runs); - hr = ScriptItemize(layout_text.c_str(), layout_text_length, runs - 1, - &script_control_, &script_state_, &script_items[0], - &script_items_count); - } - DCHECK(SUCCEEDED(hr)); - if (!SUCCEEDED(hr) || script_items_count <= 0) - return; - - // Temporarily apply composition underlines and selection colors. - ApplyCompositionAndSelectionStyles(); - - // Build the list of runs from the script items and ranged styles. Use an - // empty color BreakList to avoid breaking runs at color boundaries. - BreakList<SkColor> empty_colors; - empty_colors.SetMax(layout_text_length); - internal::StyleIterator style(empty_colors, styles()); - SCRIPT_ITEM* script_item = &script_items[0]; - const size_t max_run_length = kMaxGlyphs / 2; - for (size_t run_break = 0; run_break < layout_text_length;) { - internal::TextRun* run = new internal::TextRun(); - run->range.set_start(run_break); - run->font = font_list().GetPrimaryFont(); - run->font_style = (style.style(BOLD) ? Font::BOLD : 0) | - (style.style(ITALIC) ? Font::ITALIC : 0); - DeriveFontIfNecessary(run->font.GetFontSize(), run->font.GetHeight(), - run->font_style, &run->font); - run->strike = style.style(STRIKE); - run->diagonal_strike = style.style(DIAGONAL_STRIKE); - run->underline = style.style(UNDERLINE); - run->script_analysis = script_item->a; - - // Find the next break and advance the iterators as needed. - const size_t script_item_break = (script_item + 1)->iCharPos; - run_break = std::min(script_item_break, - TextIndexToLayoutIndex(style.GetRange().end())); - - // Clamp run lengths to avoid exceeding the maximum supported glyph count. - if ((run_break - run->range.start()) > max_run_length) { - run_break = run->range.start() + max_run_length; - if (!IsValidCodePointIndex(layout_text, run_break)) - --run_break; - } - - // Break runs adjacent to character substrings in certain code blocks. - // This avoids using their fallback fonts for more characters than needed, - // in cases like "\x25B6 Media Title", etc. http://crbug.com/278913 - if (run_break > run->range.start()) { - run_break = - FindUnusualCharacter(layout_text, run->range.start(), run_break); - } - - DCHECK(IsValidCodePointIndex(layout_text, run_break)); - - style.UpdatePosition(LayoutIndexToTextIndex(run_break)); - if (script_item_break == run_break) - script_item++; - run->range.set_end(run_break); - runs_.push_back(run); - } - - // Undo the temporarily applied composition underlines and selection colors. - UndoCompositionAndSelectionStyles(); -} - -void RenderTextWin::LayoutVisualText() { - DCHECK(!runs_.empty()); - - if (!cached_hdc_) - cached_hdc_ = CreateCompatibleDC(NULL); - - HRESULT hr = E_FAIL; - // Ensure ascent and descent are not smaller than ones of the font list. - // Keep them tall enough to draw often-used characters. - // For example, if a text field contains a Japanese character, which is - // smaller than Latin ones, and then later a Latin one is inserted, this - // ensures that the text baseline does not shift. - int ascent = font_list().GetBaseline(); - int descent = font_list().GetHeight() - font_list().GetBaseline(); - for (size_t i = 0; i < runs_.size(); ++i) { - internal::TextRun* run = runs_[i]; - LayoutTextRun(run); - - ascent = std::max(ascent, run->font.GetBaseline()); - descent = std::max(descent, - run->font.GetHeight() - 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_width_ = preceding_run_widths; -} - -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; - - run->logical_clusters.reset(new WORD[run_length]); - - // Try shaping with |original_font|. - Font current_font = original_font; - int missing_count = CountCharsWithMissingGlyphs(run, - ShapeTextRunWithFont(run, current_font)); - if (missing_count == 0) - return; - - // 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 = missing_count; - Font best_partial_font = current_font; - - // Try to shape with the cached font from previous runs, if any. - std::map<std::string, Font>::const_iterator it = - successful_substitute_fonts_.find(original_font.GetFontName()); - if (it != successful_substitute_fonts_.end()) { - current_font = it->second; - missing_count = CountCharsWithMissingGlyphs(run, - ShapeTextRunWithFont(run, current_font)); - if (missing_count == 0) - return; - if (missing_count < best_partial_font_missing_char_count) { - best_partial_font_missing_char_count = missing_count; - best_partial_font = current_font; - } - } - - // Try finding a fallback font using a meta file. - // TODO(msw|asvitkine): Support RenderText's font_list()? - Font uniscribe_font; - bool got_uniscribe_font = false; - if (GetUniscribeFallbackFont(original_font, run_text, run_length, - &uniscribe_font)) { - got_uniscribe_font = true; - current_font = uniscribe_font; - missing_count = CountCharsWithMissingGlyphs(run, - ShapeTextRunWithFont(run, current_font)); - if (missing_count == 0) { - successful_substitute_fonts_[original_font.GetFontName()] = current_font; - return; - } - if (missing_count < best_partial_font_missing_char_count) { - best_partial_font_missing_char_count = missing_count; - best_partial_font = current_font; - } - } - - // Try fonts in the fallback list except the first, which is |original_font|. - std::vector<std::string> fonts = - GetFallbackFontFamilies(original_font.GetFontName()); - for (size_t i = 1; i < fonts.size(); ++i) { - current_font = Font(fonts[i], original_font.GetFontSize()); - missing_count = CountCharsWithMissingGlyphs(run, - ShapeTextRunWithFont(run, current_font)); - if (missing_count == 0) { - successful_substitute_fonts_[original_font.GetFontName()] = current_font; - return; - } - if (missing_count < best_partial_font_missing_char_count) { - best_partial_font_missing_char_count = missing_count; - best_partial_font = current_font; - } - } - - // Try fonts in the fallback list of the Uniscribe font. - if (got_uniscribe_font) { - fonts = GetFallbackFontFamilies(uniscribe_font.GetFontName()); - for (size_t i = 1; i < fonts.size(); ++i) { - current_font = Font(fonts[i], original_font.GetFontSize()); - missing_count = CountCharsWithMissingGlyphs(run, - ShapeTextRunWithFont(run, current_font)); - if (missing_count == 0) { - successful_substitute_fonts_[original_font.GetFontName()] = - current_font; - return; - } - if (missing_count < best_partial_font_missing_char_count) { - best_partial_font_missing_char_count = missing_count; - best_partial_font = current_font; - } - } - } - - // 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); - - // The initial values for the "missing" glyph and the space glyph are taken - // from the recommendations section of the OpenType spec: - // https://www.microsoft.com/typography/otspec/recom.htm - WORD missing_glyph = 0; - WORD space_glyph = 3; - if (hr == S_OK) { - missing_glyph = properties.wgDefault; - space_glyph = properties.wgBlank; - } - - // Finally, initialize |glyph_count|, |glyphs|, |visible_attributes| and - // |logical_clusters| 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]) ? space_glyph : missing_glyph; - for (size_t i = 0; i < run_length; ++i) { - run->logical_clusters[i] = - static_cast<WORD>(run->script_analysis.fRTL ? run_length - 1 - i : i); - } - - // 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()]); - // Guess the expected number of glyphs from the length of the run. - // MSDN suggests this at 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); - // Ensure that |kMaxGlyphs| is attempted and the loop terminates afterward. - max_glyphs = std::max(max_glyphs + 1, std::min(max_glyphs * 2, kMaxGlyphs)); - } - return hr; -} - -int RenderTextWin::CountCharsWithMissingGlyphs(internal::TextRun* run, - HRESULT shaping_result) const { - if (shaping_result != S_OK) { - DCHECK_EQ(shaping_result, USP_E_SCRIPT_NOT_IN_FONT); - return INT_MAX; - } - - // 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 - int chars_not_missing_glyphs = 0; - SCRIPT_FONTPROPERTIES properties; - memset(&properties, 0, sizeof(properties)); - properties.cBytes = sizeof(properties); - ScriptGetFontProperties(cached_hdc_, &run->script_cache, &properties); - - const wchar_t* run_text = &(GetLayoutText()[run->range.start()]); - for (size_t char_index = 0; char_index < run->range.length(); ++char_index) { - const int glyph_index = run->logical_clusters[char_index]; - DCHECK_GE(glyph_index, 0); - DCHECK_LT(glyph_index, run->glyph_count); - - if (run->glyphs[glyph_index] == properties.wgDefault) - continue; - - // Windows Vista sometimes returns glyphs equal to wgBlank (instead of - // wgDefault), with fZeroWidth set. Treat such cases as having missing - // glyphs if the corresponding character is not whitespace. - // See: http://crbug.com/125629 - if (run->glyphs[glyph_index] == properties.wgBlank && - run->visible_attributes[glyph_index].fZeroWidth && - !IsWhitespace(run_text[char_index]) && - !IsUnicodeBidiControlCharacter(run_text[char_index])) { - continue; - } - - ++chars_not_missing_glyphs; - } - - DCHECK_LE(chars_not_missing_glyphs, static_cast<int>(run->range.length())); - return run->range.length() - chars_not_missing_glyphs; -} - -size_t RenderTextWin::GetRunContainingCaret(const SelectionModel& caret) const { - DCHECK(!needs_layout_); - size_t layout_position = TextIndexToLayoutIndex(caret.caret_pos()); - LogicalCursorDirection affinity = caret.caret_affinity(); - for (size_t run = 0; run < runs_.size(); ++run) - if (RangeContainsCaret(runs_[run]->range, layout_position, affinity)) - return run; - return runs_.size(); -} - -size_t RenderTextWin::GetRunContainingXCoord(int x) const { - DCHECK(!needs_layout_); - // Find the text run containing the argument point (assumed already offset). - for (size_t run = 0; run < runs_.size(); ++run) { - if ((runs_[run]->preceding_run_widths <= x) && - ((runs_[run]->preceding_run_widths + runs_[run]->width) > x)) - return run; - } - return runs_.size(); -} - -SelectionModel RenderTextWin::FirstSelectionModelInsideRun( - const internal::TextRun* run) { - size_t position = LayoutIndexToTextIndex(run->range.start()); - position = IndexOfAdjacentGrapheme(position, CURSOR_FORWARD); - return SelectionModel(position, CURSOR_BACKWARD); -} - -SelectionModel RenderTextWin::LastSelectionModelInsideRun( - const internal::TextRun* run) { - size_t position = LayoutIndexToTextIndex(run->range.end()); - position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD); - return SelectionModel(position, CURSOR_FORWARD); -} - -RenderText* RenderText::CreateNativeInstance() { - return new RenderTextWin; -} - -} // namespace gfx diff --git a/ui/gfx/render_text_win.h b/ui/gfx/render_text_win.h deleted file mode 100644 index e614594..0000000 --- a/ui/gfx/render_text_win.h +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright (c) 2012 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. - -#ifndef UI_GFX_RENDER_TEXT_WIN_H_ -#define UI_GFX_RENDER_TEXT_WIN_H_ - -#include <usp10.h> - -#include <map> -#include <string> -#include <vector> - -#include "base/memory/scoped_ptr.h" -#include "base/memory/scoped_vector.h" -#include "ui/gfx/render_text.h" - -namespace gfx { - -namespace internal { - -struct TextRun { - TextRun(); - ~TextRun(); - - 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; - - 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); -}; - -} // namespace internal - -// RenderTextWin is the Windows implementation of RenderText using Uniscribe. -class RenderTextWin : public RenderText { - public: - RenderTextWin(); - virtual ~RenderTextWin(); - - // Overridden from RenderText: - virtual scoped_ptr<RenderText> CreateInstanceOfSameType() const override; - virtual Size GetStringSize() override; - virtual SelectionModel FindCursorPosition(const Point& point) override; - virtual std::vector<FontSpan> GetFontSpansForTesting() override; - - protected: - // Overridden from RenderText: - virtual int GetLayoutTextBaseline() override; - virtual SelectionModel AdjacentCharSelectionModel( - const SelectionModel& selection, - VisualCursorDirection direction) override; - virtual SelectionModel AdjacentWordSelectionModel( - const SelectionModel& selection, - VisualCursorDirection direction) override; - virtual Range GetGlyphBounds(size_t index) override; - virtual std::vector<Rect> GetSubstringBounds(const Range& range) override; - virtual size_t TextIndexToLayoutIndex(size_t index) const override; - virtual size_t LayoutIndexToTextIndex(size_t index) const override; - virtual bool IsValidCursorIndex(size_t index) override; - virtual void ResetLayout() override; - virtual void EnsureLayout() override; - virtual void DrawVisualText(Canvas* canvas) override; - - private: - FRIEND_TEST_ALL_PREFIXES(RenderTextTest, Win_BreakRunsByUnicodeBlocks); - FRIEND_TEST_ALL_PREFIXES(RenderTextTest, Win_LogicalClusters); - FRIEND_TEST_ALL_PREFIXES(RenderTextTest, Multiline_MinWidth); - FRIEND_TEST_ALL_PREFIXES(RenderTextTest, Multiline_NormalWidth); - FRIEND_TEST_ALL_PREFIXES(RenderTextTest, BreakRunsByUnicodeBlocks); - - void ItemizeLogicalText(); - void LayoutVisualText(); - void LayoutTextRun(internal::TextRun* run); - - // Helper function that calls |ScriptShape()| on the run, which has logic to - // handle E_OUTOFMEMORY return codes. - HRESULT ShapeTextRunWithFont(internal::TextRun* run, const Font& font); - - // Returns the number of characters in |run| that have missing glyphs. - int CountCharsWithMissingGlyphs(internal::TextRun* run, - HRESULT shaping_result) const; - - // Return the run index that contains the argument; or the length of the - // |runs_| vector if argument exceeds the text length or width. - size_t GetRunContainingCaret(const SelectionModel& caret) const; - size_t GetRunContainingXCoord(int x) const; - - // Given a |run|, returns the SelectionModel that contains the logical first - // or last caret position inside (not at a boundary of) the run. - // The returned value represents a cursor/caret position without a selection. - SelectionModel FirstSelectionModelInsideRun(const internal::TextRun* run); - SelectionModel LastSelectionModelInsideRun(const internal::TextRun* run); - - // Cached HDC for performing Uniscribe API calls. - static HDC cached_hdc_; - - // Cached map from font name to the last successful substitute font used. - // TODO(asvitkine): Move the caching logic to font_fallback_win.cc. - static std::map<std::string, Font> successful_substitute_fonts_; - - SCRIPT_CONTROL script_control_; - SCRIPT_STATE script_state_; - - ScopedVector<internal::TextRun> runs_; - - // Single line width of the layout text. - int string_width_; - - // Wrapped multiline size of the layout text. - Size multiline_string_size_; - - scoped_ptr<int[]> visual_to_logical_; - scoped_ptr<int[]> logical_to_visual_; - - bool needs_layout_; - - DISALLOW_COPY_AND_ASSIGN(RenderTextWin); -}; - -} // namespace gfx - -#endif // UI_GFX_RENDER_TEXT_WIN_H_ diff --git a/ui/gfx/switches.cc b/ui/gfx/switches.cc index f36a1d5..01733dd 100644 --- a/ui/gfx/switches.cc +++ b/ui/gfx/switches.cc @@ -6,12 +6,6 @@ namespace switches { -// Disables the HarfBuzz port of RenderText on all platforms. -const char kDisableHarfBuzzRenderText[] = "disable-harfbuzz-rendertext"; - -// Enables the HarfBuzz port of RenderText on all platforms. -const char kEnableHarfBuzzRenderText[] = "enable-harfbuzz-rendertext"; - // Enable text glyphs to have X-positions that aren't snapped to the pixel grid // in webkit renderers. const char kEnableWebkitTextSubpixelPositioning[] = @@ -28,4 +22,10 @@ const char kDisableDirectWrite[] = "disable-direct-write"; const char kDisableDirectWriteForUI[] = "disable-directwrite-for-ui"; #endif +#if defined(OS_MACOSX) +// Enables the HarfBuzz port of RenderText on Mac (it's already used only for +// text editing; this enables it for everything else). +const char kEnableHarfBuzzRenderText[] = "enable-harfbuzz-rendertext"; +#endif + } // namespace switches diff --git a/ui/gfx/switches.h b/ui/gfx/switches.h index 221decaeed..5da397b 100644 --- a/ui/gfx/switches.h +++ b/ui/gfx/switches.h @@ -11,8 +11,6 @@ namespace switches { GFX_EXPORT extern const char kAllowArbitraryScaleFactorInImageSkia[]; -GFX_EXPORT extern const char kDisableHarfBuzzRenderText[]; -GFX_EXPORT extern const char kEnableHarfBuzzRenderText[]; GFX_EXPORT extern const char kEnableWebkitTextSubpixelPositioning[]; GFX_EXPORT extern const char kForceDeviceScaleFactor[]; @@ -21,6 +19,10 @@ GFX_EXPORT extern const char kDisableDirectWrite[]; GFX_EXPORT extern const char kDisableDirectWriteForUI[]; #endif +#if defined(OS_MACOSX) +GFX_EXPORT extern const char kEnableHarfBuzzRenderText[]; +#endif + } // namespace switches #endif // UI_GFX_SWITCHES_H_ diff --git a/ui/gfx/win/direct_write.cc b/ui/gfx/win/direct_write.cc index b15b59c..38c926a 100644 --- a/ui/gfx/win/direct_write.cc +++ b/ui/gfx/win/direct_write.cc @@ -65,9 +65,7 @@ void MaybeInitializeDirectWrite() { if (!ShouldUseDirectWrite() || base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableDirectWriteForUI) || - base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableHarfBuzzRenderText)) { + switches::kDisableDirectWriteForUI)) { return; } |