diff options
-rw-r--r-- | chrome/browser/autocomplete/autocomplete_popup.cc | 63 | ||||
-rw-r--r-- | chrome/browser/views/about_chrome_view.cc | 113 | ||||
-rw-r--r-- | chrome/browser/views/about_chrome_view.h | 12 | ||||
-rw-r--r-- | chrome/common/l10n_util.cc | 47 | ||||
-rw-r--r-- | chrome/common/l10n_util.h | 35 |
5 files changed, 170 insertions, 100 deletions
diff --git a/chrome/browser/autocomplete/autocomplete_popup.cc b/chrome/browser/autocomplete/autocomplete_popup.cc index e44ac90..9b61383 100644 --- a/chrome/browser/autocomplete/autocomplete_popup.cc +++ b/chrome/browser/autocomplete/autocomplete_popup.cc @@ -26,67 +26,6 @@ namespace { const int kStarPadding = 4; }; -// A simple wrapper class for the bidirectional iterator of ICU. -// This class uses the bidirctional iterator of ICU to split a line of -// bidirectional texts into visual runs in its display order. -class BiDiLineIterator { - public: - BiDiLineIterator() : bidi_(NULL) { } - ~BiDiLineIterator(); - - // Initializes the bidirectional iterator with the specified text. Returns - // whether initialization succeeded. - UBool Open(const std::wstring& text, bool right_to_left, bool url); - - // Returns the number of visual runs in the text, or zero on error. - int CountRuns(); - - // Gets the logical offset, length, and direction of the specified visual run. - UBiDiDirection GetVisualRun(int index, int* start, int* length); - - private: - UBiDi* bidi_; - - DISALLOW_EVIL_CONSTRUCTORS(BiDiLineIterator); -}; - -BiDiLineIterator::~BiDiLineIterator() { - if (bidi_) { - ubidi_close(bidi_); - bidi_ = NULL; - } -} - -UBool BiDiLineIterator::Open(const std::wstring& text, - bool right_to_left, - bool url) { - DCHECK(bidi_ == NULL); - UErrorCode error = U_ZERO_ERROR; - bidi_ = ubidi_openSized(static_cast<int>(text.length()), 0, &error); - if (U_FAILURE(error)) - return false; - if (right_to_left && url) - ubidi_setReorderingMode(bidi_, UBIDI_REORDER_RUNS_ONLY); - ubidi_setPara(bidi_, text.data(), static_cast<int>(text.length()), - right_to_left ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR, - NULL, &error); - return U_SUCCESS(error); -} - -int BiDiLineIterator::CountRuns() { - DCHECK(bidi_ != NULL); - UErrorCode error = U_ZERO_ERROR; - const int runs = ubidi_countRuns(bidi_, &error); - return U_SUCCESS(error) ? runs : 0; -} - -UBiDiDirection BiDiLineIterator::GetVisualRun(int index, - int* start, - int* length) { - DCHECK(bidi_ != NULL); - return ubidi_getVisualRun(bidi_, index, start, length); -} - // This class implements a utility used for mirroring x-coordinates when the // application language is a right-to-left one. class MirroringContext { @@ -464,7 +403,7 @@ void AutocompletePopupView::DrawMatchFragments( // Initialize a bidirectional line iterator of ICU and split the text into // visual runs. (A visual run is consecutive characters which have the same // display direction and should be displayed at once.) - BiDiLineIterator bidi_line; + l10n_util::BiDiLineIterator bidi_line; if (!bidi_line.Open(text, mirroring_context_->enabled(), url)) return; const int runs = bidi_line.CountRuns(); diff --git a/chrome/browser/views/about_chrome_view.cc b/chrome/browser/views/about_chrome_view.cc index ea962ff..6a479bc 100644 --- a/chrome/browser/views/about_chrome_view.cc +++ b/chrome/browser/views/about_chrome_view.cc @@ -75,7 +75,8 @@ AboutChromeView::AboutChromeView(Profile* profile) chromium_url_(NULL), open_source_url_(NULL), chromium_url_appears_first_(true), - check_button_status_(CHECKBUTTON_HIDDEN) { + check_button_status_(CHECKBUTTON_HIDDEN), + text_direction_is_rtl_(false) { DCHECK(profile); Init(); @@ -91,6 +92,8 @@ AboutChromeView::~AboutChromeView() { } void AboutChromeView::Init() { + text_direction_is_rtl_ = + l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT; ResourceBundle& rb = ResourceBundle::GetSharedInstance(); scoped_ptr<FileVersionInfo> version_info( @@ -339,9 +342,9 @@ void AboutChromeView::Paint(ChromeCanvas* canvas) { // Draw the second text chunk and position the Open Source url. DrawTextAndPositionUrl(canvas, main_label_chunk2_, link2, rect2, &position, label_bounds, font); - // Draw the third text chunk. - DrawTextStartingFrom(canvas, main_label_chunk3_, &position, label_bounds, - font); + // Draw the third text chunk (which has no URL associated with it). + DrawTextAndPositionUrl(canvas, main_label_chunk3_, NULL, NULL, &position, + label_bounds, font); #if defined(GOOGLE_CHROME_BUILD) // Insert a line break and some whitespace. @@ -353,8 +356,8 @@ void AboutChromeView::Paint(ChromeCanvas* canvas) { &terms_of_service_url_rect_, &position, label_bounds, font); // The last text chunk doesn't have a URL associated with it. - DrawTextStartingFrom(canvas, main_label_chunk5_, &position, label_bounds, - font); + DrawTextAndPositionUrl(canvas, main_label_chunk5_, NULL, NULL, &position, + label_bounds, font); // Position the TOS URL within the main label. terms_of_service_url_->SetBounds(terms_of_service_url_rect_.x(), @@ -386,34 +389,67 @@ void AboutChromeView::DrawTextAndPositionUrl(ChromeCanvas* canvas, gfx::Size* position, const gfx::Rect& bounds, const ChromeFont& font) { - DCHECK(canvas && link && rect && position); - // Draw the text chunk. - DrawTextStartingFrom(canvas, text, position, bounds, font); - - // And then position the link after it. - gfx::Size sz = link->GetPreferredSize(); - WrapIfWordDoesntFit(sz.width(), font.height(), position, bounds); - *rect = gfx::Rect(position->width(), position->height(), sz.width(), - sz.height()); - - // Going from relative to absolute pixel coordinates again. - rect->Offset(bounds.x(), bounds.y()); - // And leave some space to draw the link in. - position->Enlarge(sz.width(), 0); + DCHECK(canvas && position); + + // What we get passed in as |text| is potentially a mix of LTR and RTL "runs" + // (a run is a sequence of words that share the same directionality). We + // initialize a bidirectional ICU line iterator and split the text into runs + // that are either strictly LTR or strictly RTL (and do not contain a mix). + l10n_util::BiDiLineIterator bidi_line; + if (!bidi_line.Open(text.c_str(), true, false)) + return; + + // Iterate over each run and draw it. + int run_start = 0; + int run_end = 0; + const int runs = bidi_line.CountRuns(); + for (int run = 0; run < runs; ++run) { + UBiDiLevel level = 0; + bidi_line.GetLogicalRun(run_start, &run_end, &level); + std::wstring fragment = StringSubRange(text, run_start, run_end); + + // A flag that tells us whether we found LTR text inside RTL text. + bool ltr_inside_rtl_text = + ((level & 1) == UBIDI_LTR) && text_direction_is_rtl_; + + // Draw the text chunk contained in |fragment|. |position| is relative to + // the top left corner of the label we draw inside (also when drawing RTL). + DrawTextStartingFrom(canvas, fragment, position, bounds, font, + ltr_inside_rtl_text); + + run_start = run_end; // Advance over what we just drew. + } + + // If the caller is interested in placing a link after this text blurb, we + // figure out here where to place it. + if (link && rect) { + gfx::Size sz = link->GetPreferredSize(); + WrapIfWordDoesntFit(sz.width(), font.height(), position, bounds); + *rect = gfx::Rect(position->width(), position->height(), sz.width(), + sz.height()); + + // Go from relative pixel coordinates (within the label we are drawing on) + // to absolute pixel coordinates (relative to the top left corner of the + // dialog content). + rect->Offset(bounds.x(), bounds.y()); + // And leave some space to draw the link in. + position->Enlarge(sz.width(), 0); + } } void AboutChromeView::DrawTextStartingFrom(ChromeCanvas* canvas, const std::wstring& text, gfx::Size* position, const gfx::Rect& bounds, - const ChromeFont& font) { + const ChromeFont& font, + bool ltr_within_rtl) { // Iterate through line breaking opportunities (which in English would be // spaces and such. This tells us where to wrap. WordIterator iter(text, WordIterator::BREAK_LINE); if (!iter.Init()) return; - int flags = (UILayoutIsRightToLeft() ? + int flags = (text_direction_is_rtl_ ? ChromeCanvas::TEXT_ALIGN_RIGHT : ChromeCanvas::TEXT_ALIGN_LEFT) | ChromeCanvas::MULTI_LINE | @@ -423,19 +459,35 @@ void AboutChromeView::DrawTextStartingFrom(ChromeCanvas* canvas, // iterate to the next line breaking opportunity. while (iter.Advance()) { // Get the word and figure out the dimensions. - std::wstring word = iter.GetWord(); + std::wstring word; + if (!ltr_within_rtl) + word = iter.GetWord(); // Get the next word. + else + word = text; // Draw the whole text at once. + int w = font.GetStringWidth(word), h = font.height(); canvas->SizeStringInt(word, font, &w, &h, flags); // If we exceed the boundaries, we need to wrap. WrapIfWordDoesntFit(w, font.height(), position, bounds); - // Draw the word on the screen (mirrored if RTL locale). - canvas->DrawStringInt(word, font, SK_ColorBLACK, - main_text_label_->MirroredXCoordinateInsideView( - position->width() + bounds.x()), - position->height() + bounds.y(), - w, h, flags); + int x = main_text_label_->MirroredXCoordinateInsideView(position->width()) + + bounds.x(); + if (text_direction_is_rtl_) { + x -= w; + // When drawing LTR strings inside RTL text we need to make sure we draw + // the trailing space (if one exists after the LTR text) on the left of + // the LTR string. + if (ltr_within_rtl && word[word.size() - 1] == L' ') { + int space_w = font.GetStringWidth(L" "), space_h = font.height(); + canvas->SizeStringInt(L" ", font, &space_w, &space_h, flags); + x += space_w; + } + } + int y = position->height() + bounds.y(); + + // Draw the text on the screen (mirrored, if RTL run). + canvas->DrawStringInt(word, font, SK_ColorBLACK, x, y, w, h, flags); if (word.size() > 0 && word[word.size() - 1] == L'\x0a') { // When we come across '\n', we move to the beginning of the next line. @@ -445,6 +497,9 @@ void AboutChromeView::DrawTextStartingFrom(ChromeCanvas* canvas, // Otherwise, we advance position to the next word. position->Enlarge(w, 0); } + + if (ltr_within_rtl) + break; // LTR within RTL is drawn as one unit, so we are done. } } diff --git a/chrome/browser/views/about_chrome_view.h b/chrome/browser/views/about_chrome_view.h index cc29f01..be876a4 100644 --- a/chrome/browser/views/about_chrome_view.h +++ b/chrome/browser/views/about_chrome_view.h @@ -100,13 +100,16 @@ class AboutChromeView : public views::View, const ChromeFont& font); // A helper function for DrawTextAndPositionUrl, which simply draws the text - // from a certain starting point |position| and wraps within bounds. For - // details on the parameters, see DrawTextAndPositionUrl. + // from a certain starting point |position| and wraps within bounds. + // |word_for_word| specifies whether to draw the text word for word or wheter + // to treat the text as one blurb (similar to the way url's are treated inside + // RTL text. For details on the other parameters, see DrawTextAndPositionUrl. void DrawTextStartingFrom(ChromeCanvas* canvas, const std::wstring& text, gfx::Size* position, const gfx::Rect& bounds, - const ChromeFont& font); + const ChromeFont& font, + bool word_for_word); // A simply utility function that calculates whether a word of width // |word_width| fits at position |position| within the |bounds| rectangle. If @@ -162,6 +165,9 @@ class AboutChromeView : public views::View, // The version Google Update reports is available to us. std::wstring new_version_available_; + // Whether text direction is left-to-right or right-to-left. + bool text_direction_is_rtl_; + DISALLOW_COPY_AND_ASSIGN(AboutChromeView); }; diff --git a/chrome/common/l10n_util.cc b/chrome/common/l10n_util.cc index 5c6e74b..199d937 100644 --- a/chrome/common/l10n_util.cc +++ b/chrome/common/l10n_util.cc @@ -125,7 +125,7 @@ bool IsDuplicateName(const std::string& locale_name) { // Skip all 'es_RR'. Currently, we use 'es' for es-ES (Spanish in Spain). // 'es-419' (Spanish in Latin America) is not available in ICU so that it // has to be added manually in GetAvailableLocales(). - if (LowerCaseEqualsASCII(locale_name.substr(0,3), "es_")) + if (LowerCaseEqualsASCII(locale_name.substr(0, 3), "es_")) return true; for (int i = 0; i < arraysize(kDuplicateNames); ++i) { if (_stricmp(kDuplicateNames[i], locale_name.c_str()) == 0) @@ -185,7 +185,7 @@ bool CheckAndResolveLocale(const std::wstring& locale, } // Google updater uses no, iw and en for our nb, he, and en-US. - // We need to map them to our codes. + // We need to map them to our codes. struct { const char* source; const wchar_t* dest;} alias_map[] = { @@ -572,5 +572,48 @@ const std::vector<std::wstring>& GetAvailableLocales() { return locales; } +BiDiLineIterator::~BiDiLineIterator() { + if (bidi_) { + ubidi_close(bidi_); + bidi_ = NULL; + } } +UBool BiDiLineIterator::Open(const std::wstring& text, + bool right_to_left, + bool url) { + DCHECK(bidi_ == NULL); + UErrorCode error = U_ZERO_ERROR; + bidi_ = ubidi_openSized(static_cast<int>(text.length()), 0, &error); + if (U_FAILURE(error)) + return false; + if (right_to_left && url) + ubidi_setReorderingMode(bidi_, UBIDI_REORDER_RUNS_ONLY); + ubidi_setPara(bidi_, text.data(), static_cast<int>(text.length()), + right_to_left ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR, + NULL, &error); + return U_SUCCESS(error); +} + +int BiDiLineIterator::CountRuns() { + DCHECK(bidi_ != NULL); + UErrorCode error = U_ZERO_ERROR; + const int runs = ubidi_countRuns(bidi_, &error); + return U_SUCCESS(error) ? runs : 0; +} + +UBiDiDirection BiDiLineIterator::GetVisualRun(int index, + int* start, + int* length) { + DCHECK(bidi_ != NULL); + return ubidi_getVisualRun(bidi_, index, start, length); +} + +void BiDiLineIterator::GetLogicalRun(int start, + int* end, + UBiDiLevel* level) { + DCHECK(bidi_ != NULL); + ubidi_getLogicalRun(bidi_, start, end, level); +} + +} diff --git a/chrome/common/l10n_util.h b/chrome/common/l10n_util.h index 4514f60..68d1f32 100644 --- a/chrome/common/l10n_util.h +++ b/chrome/common/l10n_util.h @@ -5,14 +5,15 @@ // This file contains utility functions for dealing with localized // content. -#ifndef CHROME_COMMON_L10N_UTIL_H__ -#define CHROME_COMMON_L10N_UTIL_H__ +#ifndef CHROME_COMMON_L10N_UTIL_H_ +#define CHROME_COMMON_L10N_UTIL_H_ #include <windows.h> #include <string> #include <vector> #include "base/basictypes.h" +#include "third_party/icu38/public/common/unicode/ubidi.h" class PrefService; @@ -167,7 +168,33 @@ void SortStrings(const std::wstring& locale, // en-US, es, fr, fi, pt-PT, pt-BR, etc. const std::vector<std::wstring>& GetAvailableLocales(); -} +// A simple wrapper class for the bidirectional iterator of ICU. +// This class uses the bidirectional iterator of ICU to split a line of +// bidirectional texts into visual runs in its display order. +class BiDiLineIterator { + public: + BiDiLineIterator() : bidi_(NULL) { } + ~BiDiLineIterator(); + + // Initializes the bidirectional iterator with the specified text. Returns + // whether initialization succeeded. + UBool Open(const std::wstring& text, bool right_to_left, bool url); + + // Returns the number of visual runs in the text, or zero on error. + int CountRuns(); + + // Gets the logical offset, length, and direction of the specified visual run. + UBiDiDirection GetVisualRun(int index, int* start, int* length); + + // Given a start position, figure out where the run ends (and the BiDiLevel). + void GetLogicalRun(int start, int* end, UBiDiLevel* level); -#endif // CHROME_COMMON_L10N_UTIL_H__ + private: + UBiDi* bidi_; + + DISALLOW_COPY_AND_ASSIGN(BiDiLineIterator); +}; + +} +#endif // CHROME_COMMON_L10N_UTIL_H_ |