<rdar://problem/9134330> Missing expansion before ideograph at the beginning or end...
authormitz@apple.com <mitz@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 26 Mar 2011 00:46:26 +0000 (00:46 +0000)
committermitz@apple.com <mitz@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 26 Mar 2011 00:46:26 +0000 (00:46 +0000)
https://bugs.webkit.org/show_bug.cgi?id=57106

Reviewed by Darin Adler.

Source/WebCore:

Test: fast/text/justify-ideograph-leading-expansion.html

* platform/graphics/TextRun.h:
Replaced TrailingExpansionBehavior enum with ExpansionBehavior flags.
(WebCore::TextRun::TextRun): Constructors now take an expansionBehavior parameter.
(WebCore::TextRun::allowsLeadingExpansion): Added this accessor.
(WebCore::TextRun::allowsTrailingExpansion): Changed to use the m_expansionBehavior member.
* platform/graphics/WidthIterator.cpp:
(WebCore::WidthIterator::WidthIterator): Initialize m_isAfterExpansion from the TextRun, allowing
leading expansion when appropriate.
(WebCore::WidthIterator::advance): Moved the last-glyph-in-run check to only apply to expansion
after the glyph, not expansion before the glyph, since that is not trailing expansion. Added code
to handle expansion before the first glyph.
* platform/graphics/mac/ComplexTextController.cpp:
(WebCore::ComplexTextController::ComplexTextController): Initialize m_afterExpansion from the
TextRun, allowing leading expansion when appropriate. Set m_runWidthSoFar to the leading expansion.
(WebCore::ComplexTextController::offsetForPosition): Account for leading expansion.
(WebCore::ComplexTextController::adjustGlyphsAndAdvances): Similar to WidthIterator::advance()
* platform/graphics/mac/ComplexTextController.h: Added m_leadingExpansion member variable.
* rendering/InlineBox.h:
(WebCore::InlineBox::InlineBox): Renamed m_hasSelectedChildren to m_hasSelectedChildrenOrCanHaveLeadingExpansion
to reflect the use of this bit by InlineTextBox.
* rendering/InlineTextBox.cpp:
(WebCore::InlineTextBox::selectionRect): Replaced calls to trailingExpansionBehavior() with expansionBehavior().
(WebCore::InlineTextBox::paint): Ditto.
(WebCore::InlineTextBox::paintSelection): Ditto.
(WebCore::InlineTextBox::paintCompositionBackground): Ditto.
(WebCore::InlineTextBox::paintSpellingOrGrammarMarker): Ditto.
(WebCore::InlineTextBox::paintTextMatchMarker): Ditto.
(WebCore::InlineTextBox::computeRectForReplacementMarker): Ditto.
(WebCore::InlineTextBox::offsetForPosition): Ditto.
(WebCore::InlineTextBox::positionForOffset): Ditto.
* rendering/InlineTextBox.h:
(WebCore::InlineTextBox::canHaveLeadingExpansion): Added this accessor.
(WebCore::InlineTextBox::setCanHaveLeadingExpansion): Ditto.
(WebCore::InlineTextBox::expansionBehavior): Replaced trailingExpansionBehavior() with this function,
which also considers canHaveLeadingExpansion().
* rendering/RenderBlockLineLayout.cpp:
(WebCore::RenderBlock::computeInlineDirectionPositionsForLine): Call setCanHaveLeadingExpansion() on
text boxes that can have leading expansion. Avoid negative expansion.
* rendering/RootInlineBox.cpp:
* rendering/RootInlineBox.h:
(WebCore::RootInlineBox::hasSelectedChildren): Updated for renaming of the flag.
(WebCore::RootInlineBox::setHasSelectedChildren): Ditto.

LayoutTests:

* fast/text/justify-ideograph-leading-expansion.html: Added.
* platform/mac/fast/text/justify-ideograph-leading-expansion-expected.checksum: Added.
* platform/mac/fast/text/justify-ideograph-leading-expansion-expected.png: Added.
* platform/mac/fast/text/justify-ideograph-leading-expansion-expected.txt: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@82013 268f45cc-cd09-0410-ab3c-d52691b4dbfc

16 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/text/justify-ideograph-leading-expansion.html [new file with mode: 0644]
LayoutTests/platform/mac/fast/text/justify-ideograph-leading-expansion-expected.checksum [new file with mode: 0644]
LayoutTests/platform/mac/fast/text/justify-ideograph-leading-expansion-expected.png [new file with mode: 0644]
LayoutTests/platform/mac/fast/text/justify-ideograph-leading-expansion-expected.txt [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/TextRun.h
Source/WebCore/platform/graphics/WidthIterator.cpp
Source/WebCore/platform/graphics/mac/ComplexTextController.cpp
Source/WebCore/platform/graphics/mac/ComplexTextController.h
Source/WebCore/rendering/InlineBox.h
Source/WebCore/rendering/InlineTextBox.cpp
Source/WebCore/rendering/InlineTextBox.h
Source/WebCore/rendering/RenderBlockLineLayout.cpp
Source/WebCore/rendering/RootInlineBox.cpp
Source/WebCore/rendering/RootInlineBox.h

index 466b7cd2a416c01a9a7fdb8089f9c2f474c796de..7dd0312b53e97173ba347249a313d22ba7a37053 100644 (file)
@@ -1,3 +1,15 @@
+2011-03-25  Dan Bernstein  <mitz@apple.com>
+
+        Reviewed by Darin Adler.
+
+        <rdar://problem/9134330> Missing expansion before ideograph at the beginning or end of a text run
+        https://bugs.webkit.org/show_bug.cgi?id=57106
+
+        * fast/text/justify-ideograph-leading-expansion.html: Added.
+        * platform/mac/fast/text/justify-ideograph-leading-expansion-expected.checksum: Added.
+        * platform/mac/fast/text/justify-ideograph-leading-expansion-expected.png: Added.
+        * platform/mac/fast/text/justify-ideograph-leading-expansion-expected.txt: Added.
+
 2011-03-25  Andy Estes  <aestes@apple.com>
 
         plugins/embed-prefers-plugins-for-images.html fails on Windows.
diff --git a/LayoutTests/fast/text/justify-ideograph-leading-expansion.html b/LayoutTests/fast/text/justify-ideograph-leading-expansion.html
new file mode 100644 (file)
index 0000000..7e76e76
--- /dev/null
@@ -0,0 +1,26 @@
+<meta charset="utf-8">
+<style>
+    .test { margin: 8px 0; text-align: justify; border: solid; width: 100px; font-family: "hiragino mincho pro"; }
+</style>
+<div>
+    <div class="test">
+        a. b. <span>i</span>はxxxxxxx
+    </div>
+    <div class="test">
+        a. b. iはxxxxxxx
+    </div>
+    <div class="test">
+        xxxxxxxxxxxxxxxxxxxxx(手)
+    </div>
+</div>
+<div style="text-rendering: optimizelegibility">
+    <div class="test">
+        a. b. <span>i</span>はxxxxxxx
+    </div>
+    <div class="test">
+        a. b. iはxxxxxxx
+    </div>
+    <div class="test">
+        xxxxxxxxxxxxxxxxxxxxx(手)
+    </div>
+</div>
diff --git a/LayoutTests/platform/mac/fast/text/justify-ideograph-leading-expansion-expected.checksum b/LayoutTests/platform/mac/fast/text/justify-ideograph-leading-expansion-expected.checksum
new file mode 100644 (file)
index 0000000..2967dda
--- /dev/null
@@ -0,0 +1 @@
+43c65ac302cdf4c6e0fa39bbb5e28dd0
\ No newline at end of file
diff --git a/LayoutTests/platform/mac/fast/text/justify-ideograph-leading-expansion-expected.png b/LayoutTests/platform/mac/fast/text/justify-ideograph-leading-expansion-expected.png
new file mode 100644 (file)
index 0000000..13dc981
Binary files /dev/null and b/LayoutTests/platform/mac/fast/text/justify-ideograph-leading-expansion-expected.png differ
diff --git a/LayoutTests/platform/mac/fast/text/justify-ideograph-leading-expansion-expected.txt b/LayoutTests/platform/mac/fast/text/justify-ideograph-leading-expansion-expected.txt
new file mode 100644 (file)
index 0000000..5867399
--- /dev/null
@@ -0,0 +1,39 @@
+layer at (0,0) size 800x600
+  RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  RenderBlock {HTML} at (0,0) size 800x600
+    RenderBody {BODY} at (8,8) size 784x584
+      RenderBlock {DIV} at (0,0) size 784x154
+        RenderBlock {DIV} at (0,0) size 106x54 [border: (3px solid #000000)]
+          RenderText {#text} at (3,6) size 65x17
+            text run at (3,6) width 65: "a. b. "
+          RenderInline {SPAN} at (0,0) size 5x17
+            RenderText {#text} at (68,6) size 5x17
+              text run at (68,6) width 5: "i"
+          RenderText {#text} at (73,6) size 99x41
+            text run at (73,6) width 29: "\x{306F}"
+            text run at (3,30) width 63: "xxxxxxx"
+        RenderBlock {DIV} at (0,62) size 106x54 [border: (3px solid #000000)]
+          RenderText {#text} at (3,6) size 100x41
+            text run at (3,6) width 100: "a. b. i\x{306F}"
+            text run at (3,30) width 63: "xxxxxxx"
+        RenderBlock {DIV} at (0,124) size 106x30 [border: (3px solid #000000)]
+          RenderText {#text} at (3,6) size 237x17
+            text run at (3,6) width 237: "xxxxxxxxxxxxxxxxxxxxx\x{FF08}\x{624B}\x{FF09}"
+      RenderBlock {DIV} at (0,162) size 784x154
+        RenderBlock {DIV} at (0,0) size 106x54 [border: (3px solid #000000)]
+          RenderText {#text} at (3,6) size 65x17
+            text run at (3,6) width 65: "a. b. "
+          RenderInline {SPAN} at (0,0) size 5x17
+            RenderText {#text} at (68,6) size 5x17
+              text run at (68,6) width 5: "i"
+          RenderText {#text} at (73,6) size 99x41
+            text run at (73,6) width 29: "\x{306F}"
+            text run at (3,30) width 63: "xxxxxxx"
+        RenderBlock {DIV} at (0,62) size 106x54 [border: (3px solid #000000)]
+          RenderText {#text} at (3,6) size 100x41
+            text run at (3,6) width 100: "a. b. i\x{306F}"
+            text run at (3,30) width 63: "xxxxxxx"
+        RenderBlock {DIV} at (0,124) size 106x30 [border: (3px solid #000000)]
+          RenderText {#text} at (3,6) size 237x17
+            text run at (3,6) width 237: "xxxxxxxxxxxxxxxxxxxxx\x{FF08}\x{624B}\x{FF09}"
index c534f10a480b6e9755a480483a3a18119b136acf..a942c048eabc0d427f927161cf3f442e76a83979 100644 (file)
@@ -1,3 +1,55 @@
+2011-03-25  Dan Bernstein  <mitz@apple.com>
+
+        Reviewed by Darin Adler.
+
+        <rdar://problem/9134330> Missing expansion before ideograph at the beginning or end of a text run
+        https://bugs.webkit.org/show_bug.cgi?id=57106
+
+        Test: fast/text/justify-ideograph-leading-expansion.html
+
+        * platform/graphics/TextRun.h:
+        Replaced TrailingExpansionBehavior enum with ExpansionBehavior flags.
+        (WebCore::TextRun::TextRun): Constructors now take an expansionBehavior parameter.
+        (WebCore::TextRun::allowsLeadingExpansion): Added this accessor.
+        (WebCore::TextRun::allowsTrailingExpansion): Changed to use the m_expansionBehavior member.
+        * platform/graphics/WidthIterator.cpp:
+        (WebCore::WidthIterator::WidthIterator): Initialize m_isAfterExpansion from the TextRun, allowing
+        leading expansion when appropriate.
+        (WebCore::WidthIterator::advance): Moved the last-glyph-in-run check to only apply to expansion
+        after the glyph, not expansion before the glyph, since that is not trailing expansion. Added code
+        to handle expansion before the first glyph.
+        * platform/graphics/mac/ComplexTextController.cpp:
+        (WebCore::ComplexTextController::ComplexTextController): Initialize m_afterExpansion from the
+        TextRun, allowing leading expansion when appropriate. Set m_runWidthSoFar to the leading expansion.
+        (WebCore::ComplexTextController::offsetForPosition): Account for leading expansion.
+        (WebCore::ComplexTextController::adjustGlyphsAndAdvances): Similar to WidthIterator::advance()
+        * platform/graphics/mac/ComplexTextController.h: Added m_leadingExpansion member variable.
+        * rendering/InlineBox.h:
+        (WebCore::InlineBox::InlineBox): Renamed m_hasSelectedChildren to m_hasSelectedChildrenOrCanHaveLeadingExpansion
+        to reflect the use of this bit by InlineTextBox.
+        * rendering/InlineTextBox.cpp:
+        (WebCore::InlineTextBox::selectionRect): Replaced calls to trailingExpansionBehavior() with expansionBehavior().
+        (WebCore::InlineTextBox::paint): Ditto.
+        (WebCore::InlineTextBox::paintSelection): Ditto.
+        (WebCore::InlineTextBox::paintCompositionBackground): Ditto.
+        (WebCore::InlineTextBox::paintSpellingOrGrammarMarker): Ditto.
+        (WebCore::InlineTextBox::paintTextMatchMarker): Ditto.
+        (WebCore::InlineTextBox::computeRectForReplacementMarker): Ditto.
+        (WebCore::InlineTextBox::offsetForPosition): Ditto.
+        (WebCore::InlineTextBox::positionForOffset): Ditto.
+        * rendering/InlineTextBox.h:
+        (WebCore::InlineTextBox::canHaveLeadingExpansion): Added this accessor.
+        (WebCore::InlineTextBox::setCanHaveLeadingExpansion): Ditto.
+        (WebCore::InlineTextBox::expansionBehavior): Replaced trailingExpansionBehavior() with this function,
+        which also considers canHaveLeadingExpansion().
+        * rendering/RenderBlockLineLayout.cpp:
+        (WebCore::RenderBlock::computeInlineDirectionPositionsForLine): Call setCanHaveLeadingExpansion() on
+        text boxes that can have leading expansion. Avoid negative expansion.
+        * rendering/RootInlineBox.cpp:
+        * rendering/RootInlineBox.h:
+        (WebCore::RootInlineBox::hasSelectedChildren): Updated for renaming of the flag.
+        (WebCore::RootInlineBox::setHasSelectedChildren): Ditto.
+
 2011-03-23  Jer Noble  <jer.noble@apple.com>
 
         Reviewed by Maciej Stachowiak.
index 4e0980bf476158324efde65d1bc0fdfca4f842e5..ec763b9a3ad33e901f030dc991d3a6e0d104cea4 100644 (file)
@@ -33,17 +33,21 @@ class RenderSVGResource;
 
 class TextRun {
 public:
-    enum TrailingExpansionBehavior {
-        AllowTrailingExpansion,
-        ForbidTrailingExpansion
+    enum ExpansionBehaviorFlags {
+        ForbidTrailingExpansion = 0 << 0,
+        AllowTrailingExpansion = 1 << 0,
+        ForbidLeadingExpansion = 0 << 1,
+        AllowLeadingExpansion = 1 << 1,
     };
 
-    TextRun(const UChar* c, int len, bool allowTabs = false, float xpos = 0, float expansion = 0, TrailingExpansionBehavior trailingExpansionBehavior = AllowTrailingExpansion, bool rtl = false, bool directionalOverride = false)
+    typedef unsigned ExpansionBehavior;
+
+    TextRun(const UChar* c, int len, bool allowTabs = false, float xpos = 0, float expansion = 0, ExpansionBehavior expansionBehavior = AllowTrailingExpansion | ForbidLeadingExpansion, bool rtl = false, bool directionalOverride = false)
         : m_characters(c)
         , m_len(len)
         , m_xpos(xpos)
         , m_expansion(expansion)
-        , m_trailingExpansionBehavior(trailingExpansionBehavior)
+        , m_expansionBehavior(expansionBehavior)
 #if ENABLE(SVG)
         , m_horizontalGlyphStretch(1)
 #endif
@@ -58,12 +62,12 @@ public:
     {
     }
 
-    TextRun(const String& s, bool allowTabs = false, float xpos = 0, float expansion = 0, TrailingExpansionBehavior trailingExpansionBehavior = AllowTrailingExpansion, bool rtl = false, bool directionalOverride = false)
+    TextRun(const String& s, bool allowTabs = false, float xpos = 0, float expansion = 0, ExpansionBehavior expansionBehavior = AllowTrailingExpansion | ForbidLeadingExpansion, bool rtl = false, bool directionalOverride = false)
         : m_characters(s.characters())
         , m_len(s.length())
         , m_xpos(xpos)
         , m_expansion(expansion)
-        , m_trailingExpansionBehavior(trailingExpansionBehavior)
+        , m_expansionBehavior(expansionBehavior)
 #if ENABLE(SVG)
         , m_horizontalGlyphStretch(1)
 #endif
@@ -94,7 +98,8 @@ public:
     bool allowTabs() const { return m_allowTabs; }
     float xPos() const { return m_xpos; }
     float expansion() const { return m_expansion; }
-    bool allowsTrailingExpansion() const { return m_trailingExpansionBehavior == AllowTrailingExpansion; }
+    bool allowsLeadingExpansion() const { return m_expansionBehavior & AllowLeadingExpansion; }
+    bool allowsTrailingExpansion() const { return m_expansionBehavior & AllowTrailingExpansion; }
     bool rtl() const { return m_rtl; }
     bool ltr() const { return !m_rtl; }
     bool directionalOverride() const { return m_directionalOverride; }
@@ -121,7 +126,7 @@ private:
     // the text line is not the same as left start of the containing block.
     float m_xpos;  
     float m_expansion;
-    TrailingExpansionBehavior m_trailingExpansionBehavior;
+    ExpansionBehavior m_expansionBehavior;
 #if ENABLE(SVG)
     float m_horizontalGlyphStretch;
 #endif
index 27b062759364724c2e01ad9d040e70d9ee7db561..750a4ac1f9833a9791f7ab359b7b4f698e241fa9 100644 (file)
@@ -47,7 +47,7 @@ WidthIterator::WidthIterator(const Font* font, const TextRun& run, HashSet<const
     , m_end(run.length())
     , m_currentCharacter(0)
     , m_runWidthSoFar(0)
-    , m_isAfterExpansion(true)
+    , m_isAfterExpansion(!run.allowsLeadingExpansion())
     , m_fallbackFonts(fallbackFonts)
     , m_accountForGlyphBounds(accountForGlyphBounds)
     , m_maxGlyphBoundingBoxY(numeric_limits<float>::min())
@@ -62,7 +62,7 @@ WidthIterator::WidthIterator(const Font* font, const TextRun& run, HashSet<const
     if (!m_expansion)
         m_expansionPerOpportunity = 0;
     else {
-        bool isAfterExpansion = true;
+        bool isAfterExpansion = m_isAfterExpansion;
         unsigned expansionOpportunityCount = Font::expansionOpportunityCount(m_run.characters(), m_end, m_run.ltr() ? LTR : RTL, isAfterExpansion);
         if (isAfterExpansion && !m_run.allowsTrailingExpansion())
             expansionOpportunityCount--;
@@ -164,18 +164,24 @@ void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer)
             bool treatAsSpace = Font::treatAsSpace(c);
             if (treatAsSpace || (expandAroundIdeographs && Font::isCJKIdeographOrSymbol(c))) {
                 // Distribute the run's total expansion evenly over all expansion opportunities in the run.
-                if (m_expansion && (m_run.allowsTrailingExpansion() || (m_run.ltr() && currentCharacter + clusterLength < static_cast<size_t>(m_run.length()))
-                    || (m_run.rtl() && currentCharacter))) {
+                if (m_expansion) {
                     if (!treatAsSpace && !m_isAfterExpansion) {
                         // Take the expansion opportunity before this ideograph.
                         m_expansion -= m_expansionPerOpportunity;
                         m_runWidthSoFar += m_expansionPerOpportunity;
-                        if (glyphBuffer)
-                            glyphBuffer->expandLastAdvance(m_expansionPerOpportunity);
+                        if (glyphBuffer) {
+                            if (glyphBuffer->isEmpty())
+                                glyphBuffer->add(fontData->spaceGlyph(), fontData, m_expansionPerOpportunity);
+                            else
+                                glyphBuffer->expandLastAdvance(m_expansionPerOpportunity);
+                        }
+                    }
+                    if (m_run.allowsTrailingExpansion() || (m_run.ltr() && currentCharacter + clusterLength < static_cast<size_t>(m_run.length()))
+                        || (m_run.rtl() && currentCharacter)) {
+                        m_expansion -= m_expansionPerOpportunity;
+                        width += m_expansionPerOpportunity;
+                        m_isAfterExpansion = true;
                     }
-                    m_expansion -= m_expansionPerOpportunity;
-                    width += m_expansionPerOpportunity;
-                    m_isAfterExpansion = true;
                 } else
                     m_isAfterExpansion = false;
 
index 1a566642d8df13535d0524f0d74e571807e665c3..07bc3ecce8c4fc74b633e7692377c1d8c43ad19c 100644 (file)
@@ -63,7 +63,8 @@ ComplexTextController::ComplexTextController(const Font* font, const TextRun& ru
     , m_glyphInCurrentRun(0)
     , m_characterInCurrentGlyph(0)
     , m_expansion(run.expansion())
-    , m_afterExpansion(true)
+    , m_leadingExpansion(0)
+    , m_afterExpansion(!run.allowsLeadingExpansion())
     , m_fallbackFonts(fallbackFonts)
     , m_minGlyphBoundingBoxX(numeric_limits<float>::max())
     , m_maxGlyphBoundingBoxX(numeric_limits<float>::min())
@@ -73,7 +74,7 @@ ComplexTextController::ComplexTextController(const Font* font, const TextRun& ru
     if (!m_expansion)
         m_expansionPerOpportunity = 0;
     else {
-        bool isAfterExpansion = true;
+        bool isAfterExpansion = m_afterExpansion;
         unsigned expansionOpportunityCount = Font::expansionOpportunityCount(m_run.characters(), m_end, m_run.ltr() ? LTR : RTL, isAfterExpansion);
         if (isAfterExpansion && !m_run.allowsTrailingExpansion())
             expansionOpportunityCount--;
@@ -86,12 +87,16 @@ ComplexTextController::ComplexTextController(const Font* font, const TextRun& ru
 
     collectComplexTextRuns();
     adjustGlyphsAndAdvances();
+
+    m_runWidthSoFar = m_leadingExpansion;
 }
 
 int ComplexTextController::offsetForPosition(float h, bool includePartialGlyphs)
 {
     if (h >= m_totalWidth)
         return m_run.ltr() ? m_end : 0;
+
+    h -= m_leadingExpansion;
     if (h < 0)
         return m_run.ltr() ? 0 : m_end;
 
@@ -472,16 +477,21 @@ void ComplexTextController::adjustGlyphsAndAdvances()
                 // Handle justification and word-spacing.
                 if (treatAsSpace || Font::isCJKIdeographOrSymbol(ch)) {
                     // Distribute the run's total expansion evenly over all expansion opportunities in the run.
-                    if (m_expansion && (!lastGlyph || m_run.allowsTrailingExpansion())) {
+                    if (m_expansion) {
                         if (!treatAsSpace && !m_afterExpansion) {
                             // Take the expansion opportunity before this ideograph.
                             m_expansion -= m_expansionPerOpportunity;
                             m_totalWidth += m_expansionPerOpportunity;
-                            m_adjustedAdvances.last().width += m_expansionPerOpportunity;
+                            if (m_adjustedAdvances.isEmpty())
+                                m_leadingExpansion = m_expansionPerOpportunity;
+                            else
+                                m_adjustedAdvances.last().width += m_expansionPerOpportunity;
+                        }
+                        if (!lastGlyph || m_run.allowsTrailingExpansion()) {
+                            m_expansion -= m_expansionPerOpportunity;
+                            advance.width += m_expansionPerOpportunity;
+                            m_afterExpansion = true;
                         }
-                        m_expansion -= m_expansionPerOpportunity;
-                        advance.width += m_expansionPerOpportunity;
-                        m_afterExpansion = true;
                     } else
                         m_afterExpansion = false;
 
index 7373bfe81ca5a8f32ab84a73a8bfdd1ae33bcada..44a7994c416d1215b8efd2928558fe4e27f45028 100644 (file)
@@ -175,6 +175,7 @@ private:
     unsigned m_characterInCurrentGlyph;
     float m_expansion;
     float m_expansionPerOpportunity;
+    float m_leadingExpansion;
     bool m_afterExpansion;
 
     HashSet<const SimpleFontData*>* m_fallbackFonts;
index de7b2245659793ebca05e40e206396d7ff12025c..93359708784280fabf7774dd096a89d62fc44b23 100644 (file)
@@ -53,7 +53,7 @@ public:
 #endif
         , m_isHorizontal(true)
         , m_endsWithBreak(false)
-        , m_hasSelectedChildren(false)
+        , m_hasSelectedChildrenOrCanHaveLeadingExpansion(false)
         , m_hasEllipsisBoxOrHyphen(false)
         , m_dirOverride(false)
         , m_isText(false)
@@ -87,7 +87,7 @@ public:
 #endif
         , m_isHorizontal(isHorizontal)
         , m_endsWithBreak(false)
-        , m_hasSelectedChildren(false)   
+        , m_hasSelectedChildrenOrCanHaveLeadingExpansion(false)   
         , m_hasEllipsisBoxOrHyphen(false)
         , m_dirOverride(false)
         , m_isText(false)
@@ -340,8 +340,9 @@ protected:
 
     // for RootInlineBox
     bool m_endsWithBreak : 1;  // Whether the line ends with a <br>.
-    bool m_hasSelectedChildren : 1; // Whether we have any children selected (this bit will also be set if the <br> that terminates our line is selected).
-    bool m_hasEllipsisBoxOrHyphen : 1; 
+    // shared between RootInlineBox and InlineTextBox
+    bool m_hasSelectedChildrenOrCanHaveLeadingExpansion : 1; // Whether we have any children selected (this bit will also be set if the <br> that terminates our line is selected).
+    bool m_hasEllipsisBoxOrHyphen : 1;
 
     // for InlineTextBox
 public:
index 519d700bd5a6cd34c4f8f59d44269cbd0111faae..b614f0f82b1c79c182ade6a55742c41a298b8d79 100644 (file)
@@ -163,7 +163,7 @@ IntRect InlineTextBox::selectionRect(int tx, int ty, int startPos, int endPos)
         ePos = len;
     }
 
-    IntRect r = enclosingIntRect(f.selectionRectForText(TextRun(characters, len, textObj->allowTabs(), textPos(), m_expansion, trailingExpansionBehavior(), !isLeftToRightDirection(), m_dirOverride),
+    IntRect r = enclosingIntRect(f.selectionRectForText(TextRun(characters, len, textObj->allowTabs(), textPos(), m_expansion, expansionBehavior(), !isLeftToRightDirection(), m_dirOverride),
                                                         IntPoint(), selHeight, sPos, ePos));
                                                         
     int logicalWidth = r.width();
@@ -622,7 +622,7 @@ void InlineTextBox::paint(PaintInfo& paintInfo, int tx, int ty)
     if (hasHyphen())
         adjustCharactersAndLengthForHyphen(charactersWithHyphen, styleToUse, characters, length);
 
-    TextRun textRun(characters, length, textRenderer()->allowTabs(), textPos(), m_expansion, trailingExpansionBehavior(), !isLeftToRightDirection(), m_dirOverride || styleToUse->visuallyOrdered());
+    TextRun textRun(characters, length, textRenderer()->allowTabs(), textPos(), m_expansion, expansionBehavior(), !isLeftToRightDirection(), m_dirOverride || styleToUse->visuallyOrdered());
 
     int sPos = 0;
     int ePos = 0;
@@ -797,7 +797,7 @@ void InlineTextBox::paintSelection(GraphicsContext* context, const FloatPoint& b
     int selHeight = selectionHeight();
     FloatPoint localOrigin(boxOrigin.x(), boxOrigin.y() - deltaY);
     context->clip(FloatRect(localOrigin, FloatSize(m_logicalWidth, selHeight)));
-    context->drawHighlightForText(font, TextRun(characters, length, textRenderer()->allowTabs(), textPos(), m_expansion, trailingExpansionBehavior(), 
+    context->drawHighlightForText(font, TextRun(characters, length, textRenderer()->allowTabs(), textPos(), m_expansion, expansionBehavior(), 
                                   !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()),
                                   localOrigin, selHeight, c, style->colorSpace(), sPos, ePos);
     context->restore();
@@ -821,7 +821,7 @@ void InlineTextBox::paintCompositionBackground(GraphicsContext* context, const F
     int deltaY = renderer()->style()->isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
     int selHeight = selectionHeight();
     FloatPoint localOrigin(boxOrigin.x(), boxOrigin.y() - deltaY);
-    context->drawHighlightForText(font, TextRun(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_expansion, trailingExpansionBehavior(),
+    context->drawHighlightForText(font, TextRun(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_expansion, expansionBehavior(),
                                   !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()),
                                   localOrigin, selHeight, c, style->colorSpace(), sPos, ePos);
     context->restore();
@@ -983,7 +983,7 @@ void InlineTextBox::paintSpellingOrGrammarMarker(GraphicsContext* pt, const Floa
         int deltaY = renderer()->style()->isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
         int selHeight = selectionHeight();
         FloatPoint startPoint(boxOrigin.x(), boxOrigin.y() - deltaY);
-        TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_expansion, trailingExpansionBehavior(), !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered());
+        TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_expansion, expansionBehavior(), !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered());
         
         // FIXME: Convert the document markers to float rects.
         IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, startPoint, selHeight, startPosition, endPosition));
@@ -1028,7 +1028,7 @@ void InlineTextBox::paintTextMatchMarker(GraphicsContext* pt, const FloatPoint&
 
     int sPos = max(marker.startOffset - m_start, (unsigned)0);
     int ePos = min(marker.endOffset - m_start, (unsigned)m_len);    
-    TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_expansion, trailingExpansionBehavior(), !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered());
+    TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_expansion, expansionBehavior(), !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered());
     
     // Always compute and store the rect associated with this marker. The computed rect is in absolute coordinates.
     IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, IntPoint(m_x, selectionTop()), selHeight, sPos, ePos));
@@ -1056,7 +1056,7 @@ void InlineTextBox::computeRectForReplacementMarker(const DocumentMarker& marker
     
     int sPos = max(marker.startOffset - m_start, (unsigned)0);
     int ePos = min(marker.endOffset - m_start, (unsigned)m_len);    
-    TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_expansion, trailingExpansionBehavior(), !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered());
+    TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_expansion, expansionBehavior(), !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered());
     IntPoint startPoint = IntPoint(m_x, y);
     
     // Compute and store the rect associated with this marker.
@@ -1217,7 +1217,7 @@ int InlineTextBox::offsetForPosition(float lineOffset, bool includePartialGlyphs
     RenderStyle* style = text->style(m_firstLine);
     const Font* f = &style->font();
     int offset = f->offsetForPosition(TextRun(textRenderer()->text()->characters() + m_start, m_len,
-        textRenderer()->allowTabs(), textPos(), m_expansion, trailingExpansionBehavior(), !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()),
+        textRenderer()->allowTabs(), textPos(), m_expansion, expansionBehavior(), !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()),
         lineOffset - logicalLeft(), includePartialGlyphs);
     if (blockIsInOppositeDirection && (!offset || offset == m_len))
         return !offset ? m_len : 0;
@@ -1237,7 +1237,7 @@ float InlineTextBox::positionForOffset(int offset) const
     int from = !isLeftToRightDirection() ? offset - m_start : 0;
     int to = !isLeftToRightDirection() ? m_len : offset - m_start;
     // FIXME: Do we need to add rightBearing here?
-    return f.selectionRectForText(TextRun(text->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_expansion, trailingExpansionBehavior(), !isLeftToRightDirection(), m_dirOverride),
+    return f.selectionRectForText(TextRun(text->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_expansion, expansionBehavior(), !isLeftToRightDirection(), m_dirOverride),
                                   IntPoint(logicalLeft(), 0), 0, from, to).maxX();
 }
 
index d894b85140dcdc3a722e93b71d854c2a4f39e70d..98a1b78b7a321ad222cc797080e3860df39ed23c 100644 (file)
@@ -69,6 +69,10 @@ public:
 
     bool hasHyphen() const { return m_hasEllipsisBoxOrHyphen; }
     void setHasHyphen(bool hasHyphen) { m_hasEllipsisBoxOrHyphen = hasHyphen; }
+
+    bool canHaveLeadingExpansion() const { return m_hasSelectedChildrenOrCanHaveLeadingExpansion; }
+    void setCanHaveLeadingExpansion(bool canHaveLeadingExpansion) { m_hasSelectedChildrenOrCanHaveLeadingExpansion = canHaveLeadingExpansion; }
+
     static inline bool compareByStart(const InlineTextBox* first, const InlineTextBox* second) { return first->start() < second->start(); }
 
     virtual int baselinePosition(FontBaseline) const;
@@ -158,7 +162,11 @@ private:
     void paintTextMatchMarker(GraphicsContext*, const FloatPoint& boxOrigin, const DocumentMarker&, RenderStyle*, const Font&);
     void computeRectForReplacementMarker(const DocumentMarker&, RenderStyle*, const Font&);
 
-    TextRun::TrailingExpansionBehavior trailingExpansionBehavior() const { return m_expansion && nextLeafChild() ? TextRun::AllowTrailingExpansion : TextRun::ForbidTrailingExpansion; }
+    TextRun::ExpansionBehavior expansionBehavior() const
+    {
+        return (canHaveLeadingExpansion() ? TextRun::AllowLeadingExpansion : TextRun::ForbidLeadingExpansion)
+            | (m_expansion && nextLeafChild() ? TextRun::AllowTrailingExpansion : TextRun::ForbidTrailingExpansion);
+    }
 };
 
 inline RenderText* InlineTextBox::textRenderer() const
index 6f77218df3cfed8aace2c5feead7576844986208..9b63522d805e6c3c3037372fca06dc2120c84a3f 100644 (file)
@@ -347,6 +347,8 @@ void RenderBlock::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox,
             RenderText* rt = toRenderText(r->m_object);
 
             if (textAlign == JUSTIFY && r != trailingSpaceRun) {
+                if (!isAfterExpansion)
+                    static_cast<InlineTextBox*>(r->m_box)->setCanHaveLeadingExpansion(true);
                 unsigned opportunitiesInRun = Font::expansionOpportunityCount(rt->characters() + r->m_start, r->m_stop - r->m_start, r->m_box->direction(), isAfterExpansion);
                 expansionOpportunities.append(opportunitiesInRun);
                 expansionOpportunityCount += opportunitiesInRun;
@@ -480,7 +482,7 @@ void RenderBlock::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox,
             break;
     }
 
-    if (expansionOpportunityCount) {
+    if (expansionOpportunityCount && availableLogicalWidth > totalLogicalWidth) {
         size_t i = 0;
         for (BidiRun* r = firstRun; r; r = r->next()) {
             if (!r->m_box || r == trailingSpaceRun)
index 8673de0028430ba18236e928f27858726b363eea..4dd3a04d56e8bdd2d734a2ce8af60a1cb8f0b301 100644 (file)
@@ -356,13 +356,6 @@ GapRects RootInlineBox::lineSelectionGap(RenderBlock* rootBlock, const IntPoint&
     return result;
 }
 
-void RootInlineBox::setHasSelectedChildren(bool b)
-{
-    if (m_hasSelectedChildren == b)
-        return;
-    m_hasSelectedChildren = b;
-}
-
 RenderObject::SelectionState RootInlineBox::selectionState()
 {
     // Walk over all of the selected boxes.
index 5ab51f28458247c759b33cdcf9e0952b9b847de1..6bd3f066c656d73a4cf382585ddfb016186c9b6b 100644 (file)
@@ -99,8 +99,8 @@ public:
     virtual void paint(PaintInfo&, int tx, int ty);
     virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int, int, int, int);
 
-    bool hasSelectedChildren() const { return m_hasSelectedChildren; }
-    void setHasSelectedChildren(bool);
+    bool hasSelectedChildren() const { return m_hasSelectedChildrenOrCanHaveLeadingExpansion; }
+    void setHasSelectedChildren(bool hasSelectedChildren) { m_hasSelectedChildrenOrCanHaveLeadingExpansion = hasSelectedChildren; }
 
     virtual RenderObject::SelectionState selectionState();
     InlineBox* firstSelectedBox();