diff options
author | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-09 22:51:24 +0000 |
---|---|---|
committer | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-09 22:51:24 +0000 |
commit | e2051fde97eab43065d52214fe9e9e6fc86c761e (patch) | |
tree | 515f2c84cfcba63f5990f6c65c1986aff089fe0a | |
parent | 4b4a737b48a8ab184bc99be6a25675f565a4acbd (diff) | |
download | chromium_src-e2051fde97eab43065d52214fe9e9e6fc86c761e.zip chromium_src-e2051fde97eab43065d52214fe9e9e6fc86c761e.tar.gz chromium_src-e2051fde97eab43065d52214fe9e9e6fc86c761e.tar.bz2 |
Merge 144623 - Implement right-to-left text rendering in Pepper.
TEST=included
BUG=http://crbug.com/134394
Review URL: https://chromiumcodereview.appspot.com/10658037
TBR=brettw@chromium.org
git-svn-id: svn://svn.chromium.org/chrome/branches/1180/src@145768 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | ppapi/api/dev/ppb_font_dev.idl | 18 | ||||
-rw-r--r-- | ppapi/api/trusted/ppb_browser_font_trusted.idl | 4 | ||||
-rw-r--r-- | ppapi/c/dev/ppb_font_dev.h | 20 | ||||
-rw-r--r-- | ppapi/c/trusted/ppb_browser_font_trusted.h | 6 | ||||
-rw-r--r-- | ppapi/shared_impl/private/ppb_browser_font_trusted_shared.cc | 145 | ||||
-rw-r--r-- | ppapi/tests/test_browser_font.cc | 90 | ||||
-rw-r--r-- | ppapi/tests/test_browser_font.h | 2 |
7 files changed, 259 insertions, 26 deletions
diff --git a/ppapi/api/dev/ppb_font_dev.idl b/ppapi/api/dev/ppb_font_dev.idl index f951d8d..5d582d1 100644 --- a/ppapi/api/dev/ppb_font_dev.idl +++ b/ppapi/api/dev/ppb_font_dev.idl @@ -122,12 +122,28 @@ struct PP_TextRun_Dev { /** * Set to PP_TRUE if the text is right-to-left. + * + * When <code>override_direction</code> is false, the browser will perform + * the Unicode Bidirectional Algorithm (http://unicode.org/reports/tr9/) on + * the text. The value of the <code>rtl</code> flag specifies the + * direcionality of the surrounding environment. This means that Hebrew + * word will always display right to left, even if <code>rtl</code> is false. + * + * When <code>override_direction</code> is true, no autodetection will be done + * and <code>rtl</code> specifies the direction of the text. + * + * TODO(brettw) note that autodetection with rtl = true is currently + * unimplemented. */ PP_Bool rtl; /** * Set to PP_TRUE to force the directionality of the text regardless of - * content + * content. + * + * If this flag is set, the browser will skip autodetection of the content + * and will display all text in the direction speficied by the + * <code>rtl</code> flag. */ PP_Bool override_direction; }; diff --git a/ppapi/api/trusted/ppb_browser_font_trusted.idl b/ppapi/api/trusted/ppb_browser_font_trusted.idl index 7c0c633..79cee8c 100644 --- a/ppapi/api/trusted/ppb_browser_font_trusted.idl +++ b/ppapi/api/trusted/ppb_browser_font_trusted.idl @@ -237,6 +237,10 @@ interface PPB_BrowserFont_Trusted { * the string. This handles complex scripts such as Arabic, where characters * may be combined or replaced depending on the context. Returns (uint32)-1 * on failure. + * + * TODO(brettw) this function may be broken. See the CharPosRTL test. It + * seems to tell you "insertion point" rather than painting position. This + * is useful but maybe not what we intended here. */ uint32_t CharacterOffsetForPixel( [in] PP_Resource font, diff --git a/ppapi/c/dev/ppb_font_dev.h b/ppapi/c/dev/ppb_font_dev.h index 6ba36a6..ad85a24 100644 --- a/ppapi/c/dev/ppb_font_dev.h +++ b/ppapi/c/dev/ppb_font_dev.h @@ -3,7 +3,7 @@ * found in the LICENSE file. */ -/* From dev/ppb_font_dev.idl modified Tue Oct 11 10:01:39 2011. */ +/* From dev/ppb_font_dev.idl modified Mon Jun 25 14:54:48 2012. */ #ifndef PPAPI_C_DEV_PPB_FONT_DEV_H_ #define PPAPI_C_DEV_PPB_FONT_DEV_H_ @@ -141,11 +141,27 @@ struct PP_TextRun_Dev { struct PP_Var text; /** * Set to PP_TRUE if the text is right-to-left. + * + * When <code>override_direction</code> is false, the browser will perform + * the Unicode Bidirectional Algorithm (http://unicode.org/reports/tr9/) on + * the text. The value of the <code>rtl</code> flag specifies the + * direcionality of the surrounding environment. This means that Hebrew + * word will always display right to left, even if <code>rtl</code> is false. + * + * When <code>override_direction</code> is true, no autodetection will be done + * and <code>rtl</code> specifies the direction of the text. + * + * TODO(brettw) note that autodetection with rtl = true is currently + * unimplemented. */ PP_Bool rtl; /** * Set to PP_TRUE to force the directionality of the text regardless of - * content + * content. + * + * If this flag is set, the browser will skip autodetection of the content + * and will display all text in the direction speficied by the + * <code>rtl</code> flag. */ PP_Bool override_direction; }; diff --git a/ppapi/c/trusted/ppb_browser_font_trusted.h b/ppapi/c/trusted/ppb_browser_font_trusted.h index 080d024..c16a153 100644 --- a/ppapi/c/trusted/ppb_browser_font_trusted.h +++ b/ppapi/c/trusted/ppb_browser_font_trusted.h @@ -4,7 +4,7 @@ */ /* From trusted/ppb_browser_font_trusted.idl, - * modified Tue Feb 14 08:45:20 2012. + * modified Wed Jun 27 14:43:20 2012. */ #ifndef PPAPI_C_TRUSTED_PPB_BROWSER_FONT_TRUSTED_H_ @@ -252,6 +252,10 @@ struct PPB_BrowserFont_Trusted_1_0 { * the string. This handles complex scripts such as Arabic, where characters * may be combined or replaced depending on the context. Returns (uint32)-1 * on failure. + * + * TODO(brettw) this function may be broken. See the CharPosRTL test. It + * seems to tell you "insertion point" rather than painting position. This + * is useful but maybe not what we intended here. */ uint32_t (*CharacterOffsetForPixel)( PP_Resource font, diff --git a/ppapi/shared_impl/private/ppb_browser_font_trusted_shared.cc b/ppapi/shared_impl/private/ppb_browser_font_trusted_shared.cc index e5414b3..9b2c0f5 100644 --- a/ppapi/shared_impl/private/ppb_browser_font_trusted_shared.cc +++ b/ppapi/shared_impl/private/ppb_browser_font_trusted_shared.cc @@ -21,6 +21,7 @@ #include "third_party/WebKit/Source/WebKit/chromium/public/WebFont.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebFontDescription.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebTextRun.h" +#include "unicode/ubidi.h" using ppapi::StringVar; using ppapi::thunk::EnterResourceNoLock; @@ -51,6 +52,74 @@ string16 GetFontFromMap( return string16(); } +// Splits a PP_BrowserFont_Trusted_TextRun into a sequence or LTR and RTL +// WebTextRuns that can be used for WebKit. Normally WebKit does this for us, +// but the font drawing and measurement routines we call happen after this +// step. So for correct rendering of RTL content, we need to do it ourselves. +class TextRunCollection { + public: + explicit TextRunCollection(const PP_BrowserFont_Trusted_TextRun& run) + : bidi_(NULL), + num_runs_(0) { + StringVar* text_string = StringVar::FromPPVar(run.text); + if (!text_string) + return; // Leave num_runs_ = 0 so we'll do nothing. + text_ = UTF8ToUTF16(text_string->value()); + + if (run.override_direction) { + // Skip autodetection. + num_runs_ = 1; + override_run_ = WebTextRun(text_, PP_ToBool(run.rtl), true); + } else { + bidi_ = ubidi_open(); + UErrorCode uerror = U_ZERO_ERROR; + ubidi_setPara(bidi_, text_.data(), text_.size(), run.rtl, NULL, &uerror); + if (U_SUCCESS(uerror)) + num_runs_ = ubidi_countRuns(bidi_, &uerror); + } + } + + ~TextRunCollection() { + if (bidi_) + ubidi_close(bidi_); + } + + const string16& text() const { return text_; } + int num_runs() const { return num_runs_; } + + // Returns a WebTextRun with the info for the run at the given index. + // The range covered by the run is in the two output params. + WebTextRun GetRunAt(int index, int32_t* run_start, int32_t* run_len) const { + DCHECK(index < num_runs_); + if (bidi_) { + bool run_rtl = !!ubidi_getVisualRun(bidi_, index, run_start, run_len); + return WebTextRun(string16(&text_[*run_start], *run_len), + run_rtl, true); + } + + // Override run, return the single one. + DCHECK(index == 0); + *run_start = 0; + *run_len = static_cast<int32_t>(text_.size()); + return override_run_; + } + + private: + // Will be null if we skipped autodetection. + UBiDi* bidi_; + + // Text of all the runs. + string16 text_; + + int num_runs_; + + // When the content specifies override_direction (bidi_ is null) then this + // will contain the single text run for WebKit. + WebTextRun override_run_; + + DISALLOW_COPY_AND_ASSIGN(TextRunCollection); +}; + bool PPTextRunToWebTextRun(const PP_BrowserFont_Trusted_TextRun& text, WebTextRun* run) { StringVar* text_string = StringVar::FromPPVar(text.text); @@ -280,25 +349,53 @@ int32_t PPB_BrowserFont_Trusted_Shared::MeasureText( uint32_t PPB_BrowserFont_Trusted_Shared::CharacterOffsetForPixel( const PP_BrowserFont_Trusted_TextRun* text, int32_t pixel_position) { - WebTextRun run; - if (!PPTextRunToWebTextRun(*text, &run)) - return -1; - return static_cast<uint32_t>(font_->offsetForPosition( - run, static_cast<float>(pixel_position))); + TextRunCollection runs(*text); + int32_t cur_pixel_offset = 0; + for (int i = 0; i < runs.num_runs(); i++) { + int32_t run_begin = 0; + int32_t run_len = 0; + WebTextRun run = runs.GetRunAt(i, &run_begin, &run_len); + int run_width = font_->calculateWidth(run); + if (pixel_position < cur_pixel_offset + run_width) { + // Offset is in this run. + return static_cast<uint32_t>(font_->offsetForPosition( + run, static_cast<float>(pixel_position - cur_pixel_offset))) + + run_begin; + } + cur_pixel_offset += run_width; + } + return runs.text().size(); } int32_t PPB_BrowserFont_Trusted_Shared::PixelOffsetForCharacter( const PP_BrowserFont_Trusted_TextRun* text, uint32_t char_offset) { - WebTextRun run; - if (!PPTextRunToWebTextRun(*text, &run)) - return -1; - if (char_offset >= run.text.length()) - return -1; - - WebFloatRect rect = font_->selectionRectForText( - run, WebFloatPoint(0.0f, 0.0f), font_->height(), 0, char_offset); - return static_cast<int>(rect.width); + TextRunCollection runs(*text); + int32_t cur_pixel_offset = 0; + for (int i = 0; i < runs.num_runs(); i++) { + int32_t run_begin = 0; + int32_t run_len = 0; + WebTextRun run = runs.GetRunAt(i, &run_begin, &run_len); + if (char_offset >= static_cast<uint32_t>(run_begin) && + char_offset < static_cast<uint32_t>(run_begin + run_len)) { + // Character we're looking for is in this run. + // + // Here we ask WebKit to give us the rectangle around the character in + // question, and then return the left edge. If we asked for a range of + // 0 characters starting at the character in question, it would give us + // a 0-width rect around the insertion point. But that will be on the + // right side of the character for an RTL run, which would be wrong. + WebFloatRect rect = font_->selectionRectForText( + run, WebFloatPoint(0.0f, 0.0f), font_->height(), + char_offset - run_begin, char_offset - run_begin + 1); + return cur_pixel_offset + static_cast<int>(rect.x); + } else { + // Character is past this run, account for the pixels and continue + // looking. + cur_pixel_offset += font_->calculateWidth(run); + } + } + return -1; // Requested a char beyond the end. } void PPB_BrowserFont_Trusted_Shared::DrawTextToCanvas( @@ -308,10 +405,6 @@ void PPB_BrowserFont_Trusted_Shared::DrawTextToCanvas( uint32_t color, const PP_Rect* clip, PP_Bool image_data_is_opaque) { - WebTextRun run; - if (!PPTextRunToWebTextRun(text, &run)) - return; - // Convert position and clip. WebFloatPoint web_position(static_cast<float>(position->x), static_cast<float>(position->y)); @@ -328,8 +421,20 @@ void PPB_BrowserFont_Trusted_Shared::DrawTextToCanvas( clip->size.width, clip->size.height); } - font_->drawText(destination, run, web_position, color, web_clip, - PP_ToBool(image_data_is_opaque)); + TextRunCollection runs(text); + for (int i = 0; i < runs.num_runs(); i++) { + int32_t run_begin = 0; + int32_t run_len = 0; + WebTextRun run = runs.GetRunAt(i, &run_begin, &run_len); + font_->drawText(destination, run, web_position, color, web_clip, + PP_ToBool(image_data_is_opaque)); + + // Advance to the next run. Note that we avoid doing this for the last run + // since it's unnecessary, measuring text is slow, and most of the time + // there will be only one run anyway. + if (i != runs.num_runs() - 1) + web_position.x += font_->calculateWidth(run); + } } } // namespace ppapi diff --git a/ppapi/tests/test_browser_font.cc b/ppapi/tests/test_browser_font.cc index 9f3a2e0..8fc4104 100644 --- a/ppapi/tests/test_browser_font.cc +++ b/ppapi/tests/test_browser_font.cc @@ -4,8 +4,6 @@ #include "ppapi/tests/test_browser_font.h" -#include <stdio.h>// ERASEME - #include "ppapi/tests/test_utils.h" #include "ppapi/tests/testing_instance.h" #include "ppapi/cpp/image_data.h" @@ -20,7 +18,11 @@ bool TestBrowserFont::Init() { void TestBrowserFont::RunTests(const std::string& filter) { RUN_TEST(FontFamilies, filter); RUN_TEST(Measure, filter); + RUN_TEST(MeasureRTL, filter); RUN_TEST(CharPos, filter); + // This test is disabled. It doesn't currently pass. See the + // CharacterOffsetForPixel API. + //RUN_TEST(CharPosRTL, filter); RUN_TEST(Draw, filter); } @@ -54,6 +56,48 @@ std::string TestBrowserFont::TestMeasure() { PASS(); } +std::string TestBrowserFont::TestMeasureRTL() { + pp::BrowserFontDescription desc; + pp::BrowserFont_Trusted font(instance_, desc); + + // Mixed string, two chars of LTR, two of RTL, then two of LTR. + // Note this is in UTF-8 so has more than 6 bytes. + std::string mixed("AB\xd7\x94\xd7\x97ZZ"); + const int kNumChars = 6; + pp::BrowserFontTextRun run(mixed); + + // Note that since this is UTF-8, the two RTL chars are two bytes each. + int32_t len[kNumChars]; + len[0] = font.PixelOffsetForCharacter(run, 0); + len[1] = font.PixelOffsetForCharacter(run, 1); + len[2] = font.PixelOffsetForCharacter(run, 2); + len[3] = font.PixelOffsetForCharacter(run, 3); + len[4] = font.PixelOffsetForCharacter(run, 4); + len[5] = font.PixelOffsetForCharacter(run, 5); + + // First three chars should be increasing. + ASSERT_TRUE(len[0] >= 0); + ASSERT_TRUE(len[1] > len[0]); + ASSERT_TRUE(len[3] > len[1]); + ASSERT_TRUE(len[2] > len[3]); + ASSERT_TRUE(len[4] > len[2]); + ASSERT_TRUE(len[5] > len[4]); + + // Test the same sequence with force LTR. The offsets should appear in + // sequence. + pp::BrowserFontTextRun forced_run(mixed, false, true); + len[0] = font.PixelOffsetForCharacter(forced_run, 0); + len[1] = font.PixelOffsetForCharacter(forced_run, 1); + len[2] = font.PixelOffsetForCharacter(forced_run, 2); + len[3] = font.PixelOffsetForCharacter(forced_run, 3); + len[4] = font.PixelOffsetForCharacter(forced_run, 4); + len[5] = font.PixelOffsetForCharacter(forced_run, 5); + for (int i = 1; i < kNumChars; i++) + ASSERT_TRUE(len[i] > len[i - 1]); + + PASS(); +} + // Tests that the character/pixel offset functions correctly round-trip. std::string TestBrowserFont::TestCharPos() { pp::BrowserFontDescription desc; @@ -71,6 +115,48 @@ std::string TestBrowserFont::TestCharPos() { PASS(); } +// Tests that we can get character positions in a mixed LTR/RTL run. +std::string TestBrowserFont::TestCharPosRTL() { + pp::BrowserFontDescription desc; + pp::BrowserFont_Trusted font(instance_, desc); + + // Mixed string, two chars of LTR, two of RTL, than two of LTR. + // Note this is in UTF-8 so has more than 6 bytes. + std::string mixed("AB\xd7\x94\xd7\x97ZZ"); + + pp::BrowserFontTextRun run(mixed); + static const int kNumChars = 6; + int expected_char_sequence[kNumChars] = { 0, 1, 3, 2, 4, 5 }; + + // Check that the characters appear in the order we expect. + int pixel_width = font.MeasureText(pp::BrowserFontTextRun(mixed)); + int last_sequence = 0; // Index into expected_char_sequence. + for (int x = 0; x < pixel_width; x++) { + int cur_char = font.CharacterOffsetForPixel(run, x); + if (cur_char != expected_char_sequence[last_sequence]) { + // This pixel has a different character. It should be the next one in + // the sequence for it to be correct. + last_sequence++; + ASSERT_TRUE(last_sequence < kNumChars); + ASSERT_TRUE(cur_char == expected_char_sequence[last_sequence]); + } + } + + // Try the same string with force LTR. The characters should all appear in + // sequence. + pp::BrowserFontTextRun forced_run(mixed, false, true); + int last_forced_char = 0; // Char index into the forced sequence. + for (int x = 0; x < pixel_width; x++) { + int cur_char = font.CharacterOffsetForPixel(forced_run, x); + if (cur_char != last_forced_char) { + last_forced_char++; + ASSERT_TRUE(cur_char == last_forced_char); + } + } + + PASS(); +} + // Tests that drawing some text produces "some" output. std::string TestBrowserFont::TestDraw() { pp::BrowserFontDescription desc; diff --git a/ppapi/tests/test_browser_font.h b/ppapi/tests/test_browser_font.h index eba1c7e..c166212 100644 --- a/ppapi/tests/test_browser_font.h +++ b/ppapi/tests/test_browser_font.h @@ -18,7 +18,9 @@ class TestBrowserFont : public TestCase { private: std::string TestFontFamilies(); std::string TestMeasure(); + std::string TestMeasureRTL(); std::string TestCharPos(); + std::string TestCharPosRTL(); std::string TestDraw(); }; |