Title: [197464] trunk
Revision
197464
Author
[email protected]
Date
2016-03-02 14:29:26 -0800 (Wed, 02 Mar 2016)

Log Message

Add support for the "first" value of the hanging-punctuation property.
https://bugs.webkit.org/show_bug.cgi?id=154919

Reviewed by Simon Fraser.

Source/WebCore:

New tests added in fast/text.

Implement the "first" value for hanging-punctuation as described here:
https://drafts.csswg.org/css-text-3/#propdef-hanging-punctuation

* rendering/RenderBlockFlow.cpp:
(WebCore::RenderBlockFlow::computeInlinePreferredLogicalWidths):
Update the preferred logical width computation to factor in hanging punctuation.
This check is similar to the text-indent logic in that we only want to do it for
the first formatted line.

* rendering/RenderBlockFlow.h:
(WebCore::RenderBlockFlow::simpleLineLayout):
Make sure to turn off simple line layout when hanging punctuation is present. Eventually
it should be feasible to support this in simple line layout, but since the full line
layout model has to work with it anyway, we are starting there.

* rendering/RenderBlockLineLayout.cpp:
(WebCore::inlineAncestorHasStartBorderPaddingOrMargin):
(WebCore::isLastInFlowRun):
Helper functions that are needed to determine whether or not we're allowed to apply
hanging punctuation "first" to a text run.

(WebCore::RenderBlockFlow::computeInlineDirectionPositionsForSegment):
This function manipulates logicalLeft and availableWidth when hanging punctuation
is present to shift the line as needed and to expand the availableWidth of the line.

* rendering/RenderText.cpp:
(WebCore::isHangablePunctuationAtLineStart):
(WebCore::isHangablePunctuationAtLineEnd):
(WebCore::RenderText::hangablePunctuationStartWidth):
(WebCore::RenderText::trimmedPrefWidths):
* rendering/RenderText.h:
RenderText has a helper function for handing back the hangable punctuation width. This
is used everywhere line layout wants to apply that offset. There are also helper functions
that detect whether the character is a hangable punctuation character.

* rendering/SimpleLineLayout.cpp:
(WebCore::SimpleLineLayout::canUseForWithReason):
(WebCore::SimpleLineLayout::printReason):
Turn off simple line layout when hanging punctuation is enabled.

* rendering/line/BreakingContext.h:
(WebCore::BreakingContext::handleText):
Modified to expand the available width when hanging punctuation is present so that we
know we have more room on the line.

* rendering/line/LineWidth.h:
(WebCore::LineWidth::isFirstLine):
Add an accessor for whether or not we're the first line.

LayoutTests:

* fast/text/hanging-punctuation-first-expected.html: Added.
* fast/text/hanging-punctuation-first-rtl-expected.html: Added.
* fast/text/hanging-punctuation-first-rtl.html: Added.
* fast/text/hanging-punctuation-first.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (197463 => 197464)


--- trunk/LayoutTests/ChangeLog	2016-03-02 21:56:47 UTC (rev 197463)
+++ trunk/LayoutTests/ChangeLog	2016-03-02 22:29:26 UTC (rev 197464)
@@ -1,3 +1,15 @@
+2016-03-02  Dave Hyatt  <[email protected]>
+
+        Add support for the "first" value of the hanging-punctuation property.
+        https://bugs.webkit.org/show_bug.cgi?id=154919
+
+        Reviewed by Simon Fraser.
+
+        * fast/text/hanging-punctuation-first-expected.html: Added.
+        * fast/text/hanging-punctuation-first-rtl-expected.html: Added.
+        * fast/text/hanging-punctuation-first-rtl.html: Added.
+        * fast/text/hanging-punctuation-first.html: Added.
+
 2016-03-01  Ryosuke Niwa  <[email protected]>
 
         Make HTML parser construct custom elements

Added: trunk/LayoutTests/fast/text/hanging-punctuation-first-expected.html (0 => 197464)


--- trunk/LayoutTests/fast/text/hanging-punctuation-first-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/text/hanging-punctuation-first-expected.html	2016-03-02 22:29:26 UTC (rev 197464)
@@ -0,0 +1,14 @@
+<head>
+<style>
+    body { font-family: 'Ahem'; color:green }
+    .hang { text-indent: -1em; margin:1em }
+</style>
+</head>
+<body>
+<div style="float:left" class="hang">(Hang test)</div>
+<div style="clear:both">
+<div class="hang">(This should hang.<br>(This should not.</div>
+<div class="hang" style="text-align:justify; width:300px;">(This should hang and justifybecause and now were fine.</div>
+<div class="hang" style="text-align:justify; width:300px;"><span>(</span>This should hang.</div>
+<div style="margin-left:1em; text-align:justify; width:300px;"><span style="border-left:1em solid blue">(</span>This should not hang.</div>
+<div class="hang" style="text-indent:1em; text-align:justify; width:300px;">(This should hang into the text-indent.</div>
\ No newline at end of file

Added: trunk/LayoutTests/fast/text/hanging-punctuation-first-rtl-expected.html (0 => 197464)


--- trunk/LayoutTests/fast/text/hanging-punctuation-first-rtl-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/text/hanging-punctuation-first-rtl-expected.html	2016-03-02 22:29:26 UTC (rev 197464)
@@ -0,0 +1,14 @@
+<head>
+<style>
+    body { font-family: 'Ahem'; color:green }
+    .hang { text-indent: -1em; margin:1em }
+</style>
+</head>
+<body style="direction:rtl">
+<div style="float:right" class="hang">(Hang test)</div>
+<div style="clear:both">
+<div class="hang">(This should hang.<br>(This should not.</div>
+<div class="hang" style="text-align:justify; width:300px;">(This should hang and justifybecause and now were fine.</div>
+<div class="hang" style="text-align:justify; width:300px;"><span>(</span>This should hang.</div>
+<div style="margin-right:1em; text-align:justify; width:300px;"><span style="border-right:1em solid blue">(</span>This should not hang.</div>
+<div class="hang" style="text-indent:1em; text-align:justify; width:300px;">(This should hang into the text-indent.</div>
\ No newline at end of file

Added: trunk/LayoutTests/fast/text/hanging-punctuation-first-rtl.html (0 => 197464)


--- trunk/LayoutTests/fast/text/hanging-punctuation-first-rtl.html	                        (rev 0)
+++ trunk/LayoutTests/fast/text/hanging-punctuation-first-rtl.html	2016-03-02 22:29:26 UTC (rev 197464)
@@ -0,0 +1,14 @@
+<head>
+<style>
+    body { font-family: 'Ahem'; color:green }
+    .hang { hanging-punctuation: first; margin:1em }
+</style>
+</head>
+<body style="direction:rtl">
+<div style="float:right" class="hang">(Hang test)</div>
+<div style="clear:both">
+<div class="hang">(This should hang.<br>(This should not.</div>
+<div class="hang" style="text-align:justify; width:300px;">(This should hang and justifybecause and now were fine.</div>
+<div class="hang" style="text-align:justify; width:300px;"><span>(</span>This should hang.</div>
+<div class="hang" style="text-align:justify; width:300px;"><span style="border-right:1em solid blue">(</span>This should not hang.</div>
+<div class="hang" style="text-indent:2em; text-align:justify; width:300px;">(This should hang into the text-indent.</div>
\ No newline at end of file

Added: trunk/LayoutTests/fast/text/hanging-punctuation-first.html (0 => 197464)


--- trunk/LayoutTests/fast/text/hanging-punctuation-first.html	                        (rev 0)
+++ trunk/LayoutTests/fast/text/hanging-punctuation-first.html	2016-03-02 22:29:26 UTC (rev 197464)
@@ -0,0 +1,14 @@
+<head>
+<style>
+    body { font-family: 'Ahem'; color:green }
+    .hang { hanging-punctuation: first; margin:1em }
+</style>
+</head>
+<body>
+<div style="float:left" class="hang">(Hang test)</div>
+<div style="clear:both">
+<div class="hang">(This should hang.<br>(This should not.</div>
+<div class="hang" style="text-align:justify; width:300px;">(This should hang and justifybecause and now were fine.</div>
+<div class="hang" style="text-align:justify; width:300px;"><span>(</span>This should hang.</div>
+<div class="hang" style="text-align:justify; width:300px;"><span style="border-left:1em solid blue">(</span>This should not hang.</div>
+<div class="hang" style="text-indent:2em; text-align:justify; width:300px;">(This should hang into the text-indent.</div>
\ No newline at end of file

Modified: trunk/Source/WebCore/ChangeLog (197463 => 197464)


--- trunk/Source/WebCore/ChangeLog	2016-03-02 21:56:47 UTC (rev 197463)
+++ trunk/Source/WebCore/ChangeLog	2016-03-02 22:29:26 UTC (rev 197464)
@@ -1,3 +1,61 @@
+2016-03-02  Dave Hyatt  <[email protected]>
+
+        Add support for the "first" value of the hanging-punctuation property.
+        https://bugs.webkit.org/show_bug.cgi?id=154919
+
+        Reviewed by Simon Fraser.
+
+        New tests added in fast/text.
+
+        Implement the "first" value for hanging-punctuation as described here:
+        https://drafts.csswg.org/css-text-3/#propdef-hanging-punctuation
+
+        * rendering/RenderBlockFlow.cpp:
+        (WebCore::RenderBlockFlow::computeInlinePreferredLogicalWidths):
+        Update the preferred logical width computation to factor in hanging punctuation.
+        This check is similar to the text-indent logic in that we only want to do it for
+        the first formatted line.
+
+        * rendering/RenderBlockFlow.h:
+        (WebCore::RenderBlockFlow::simpleLineLayout):
+        Make sure to turn off simple line layout when hanging punctuation is present. Eventually
+        it should be feasible to support this in simple line layout, but since the full line
+        layout model has to work with it anyway, we are starting there.
+
+        * rendering/RenderBlockLineLayout.cpp:
+        (WebCore::inlineAncestorHasStartBorderPaddingOrMargin):
+        (WebCore::isLastInFlowRun):
+        Helper functions that are needed to determine whether or not we're allowed to apply
+        hanging punctuation "first" to a text run.
+
+        (WebCore::RenderBlockFlow::computeInlineDirectionPositionsForSegment):
+        This function manipulates logicalLeft and availableWidth when hanging punctuation
+        is present to shift the line as needed and to expand the availableWidth of the line.
+
+        * rendering/RenderText.cpp:
+        (WebCore::isHangablePunctuationAtLineStart):
+        (WebCore::isHangablePunctuationAtLineEnd):
+        (WebCore::RenderText::hangablePunctuationStartWidth):
+        (WebCore::RenderText::trimmedPrefWidths):
+        * rendering/RenderText.h:
+        RenderText has a helper function for handing back the hangable punctuation width. This
+        is used everywhere line layout wants to apply that offset. There are also helper functions
+        that detect whether the character is a hangable punctuation character.
+
+        * rendering/SimpleLineLayout.cpp:
+        (WebCore::SimpleLineLayout::canUseForWithReason):
+        (WebCore::SimpleLineLayout::printReason):
+        Turn off simple line layout when hanging punctuation is enabled.
+
+        * rendering/line/BreakingContext.h:
+        (WebCore::BreakingContext::handleText):
+        Modified to expand the available width when hanging punctuation is present so that we
+        know we have more room on the line.
+
+        * rendering/line/LineWidth.h:
+        (WebCore::LineWidth::isFirstLine):
+        Add an accessor for whether or not we're the first line.
+
 2016-03-01  Ryosuke Niwa  <[email protected]>
 
         Make HTML parser construct custom elements

Modified: trunk/Source/WebCore/rendering/RenderBlockFlow.cpp (197463 => 197464)


--- trunk/Source/WebCore/rendering/RenderBlockFlow.cpp	2016-03-02 21:56:47 UTC (rev 197463)
+++ trunk/Source/WebCore/rendering/RenderBlockFlow.cpp	2016-03-02 22:29:26 UTC (rev 197464)
@@ -4064,6 +4064,9 @@
     RenderObject* prevFloat = 0;
     bool isPrevChildInlineFlow = false;
     bool shouldBreakLineAfterText = false;
+    bool canHangPunctuationAtStart = styleToUse.hangingPunctuation() & FirstHangingPunctuation;
+    bool addedStartPunctuationHang = false;
+    
     while (RenderObject* child = childIterator.next()) {
         bool autoWrap = child->isReplaced() ? child->parent()->style().autoWrap() :
             child->style().autoWrap();
@@ -4179,6 +4182,9 @@
                     else
                         addedTextIndent = true;
                 }
+                
+                if (canHangPunctuationAtStart && !addedStartPunctuationHang && !child->isFloating() && !isAnonymousInlineBlock)
+                    addedStartPunctuationHang = true;
 
                 // Add our width to the max.
                 inlineMax += std::max<float>(0, childMax);
@@ -4267,7 +4273,17 @@
                         hasRemainingNegativeTextIndent = true;
                     }
                 }
-
+                
+                // See if we have a hanging punctuation situation at the start.
+                if (canHangPunctuationAtStart && !addedStartPunctuationHang) {
+                    float hangStartWidth = renderText.hangablePunctuationStartWidth();
+                    childMin -= hangStartWidth;
+                    beginMin -= hangStartWidth;
+                    childMax -= hangStartWidth;
+                    beginMax -= hangStartWidth;
+                    addedStartPunctuationHang = true;
+                }
+                
                 // If we have no breakable characters at all,
                 // then this is the easy case. We add ourselves to the current
                 // min and max and continue.
@@ -4305,6 +4321,7 @@
                     maxLogicalWidth = preferredWidth(maxLogicalWidth, childMax);
                     inlineMax = endMax;
                     addedTextIndent = true;
+                    addedStartPunctuationHang = true;
                 } else
                     inlineMax += std::max<float>(0, childMax);
             }
@@ -4319,6 +4336,7 @@
             stripFrontSpaces = true;
             trailingSpaceChild = 0;
             addedTextIndent = true;
+            addedStartPunctuationHang = true;
         }
 
         if (!child->isText() && child->isRenderInline())

Modified: trunk/Source/WebCore/rendering/RenderBlockLineLayout.cpp (197463 => 197464)


--- trunk/Source/WebCore/rendering/RenderBlockLineLayout.cpp	2016-03-02 21:56:47 UTC (rev 197463)
+++ trunk/Source/WebCore/rendering/RenderBlockLineLayout.cpp	2016-03-02 22:29:26 UTC (rev 197464)
@@ -806,11 +806,34 @@
     }
 }
 
+static bool inlineAncestorHasStartBorderPaddingOrMargin(const RenderBlockFlow& block, const InlineBox& box)
+{
+    bool isLTR = block.style().isLeftToRightDirection();
+    for (auto* currentBox = box.parent(); currentBox; currentBox = currentBox->parent()) {
+        if ((isLTR && currentBox->marginBorderPaddingLogicalLeft() > 0)
+            || (!isLTR && currentBox->marginBorderPaddingLogicalRight() > 0))
+            return true;
+    }
+    return false;
+}
+
+static bool isLastInFlowRun(BidiRun& runToCheck)
+{
+    for (auto* run = runToCheck.next(); run; run = run->next()) {
+        if (!run->box() || run->renderer().isOutOfFlowPositioned() || run->box()->isLineBreak())
+            continue;
+        return false;
+    }
+    return true;
+}
+    
 BidiRun* RenderBlockFlow::computeInlineDirectionPositionsForSegment(RootInlineBox* lineBox, const LineInfo& lineInfo, ETextAlign textAlign, float& logicalLeft, 
     float& availableLogicalWidth, BidiRun* firstRun, BidiRun* trailingSpaceRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache,
     WordMeasurements& wordMeasurements)
 {
     bool needsWordSpacing = false;
+    bool canHangPunctuationAtStart = style().hangingPunctuation() & FirstHangingPunctuation;
+    bool isLTR = style().isLeftToRightDirection();
     float totalLogicalWidth = lineBox->getFlowSpacingLogicalWidth();
     unsigned expansionOpportunityCount = 0;
     bool isAfterExpansion = is<RenderRubyBase>(*this) ? downcast<RenderRubyBase>(*this).isAfterExpansion() : true;
@@ -827,6 +850,15 @@
         if (is<RenderText>(run->renderer())) {
             auto& renderText = downcast<RenderText>(run->renderer());
             auto& textBox = downcast<InlineTextBox>(*run->box());
+            if (canHangPunctuationAtStart && lineInfo.isFirstLine() && (isLTR || isLastInFlowRun(*run))
+                && !inlineAncestorHasStartBorderPaddingOrMargin(*this, *run->box())) {
+                float hangStartWidth = renderText.hangablePunctuationStartWidth();
+                availableLogicalWidth += hangStartWidth;
+                if (style().isLeftToRightDirection())
+                    logicalLeft -= hangStartWidth;
+                canHangPunctuationAtStart = false;
+            }
+            
             if (textAlign == JUSTIFY && run != trailingSpaceRun) {
                 ExpansionBehavior expansionBehavior = expansionBehaviorForInlineTextBox(*this, textBox, previousRun, run->next(), textAlign, isAfterExpansion);
                 applyExpansionBehavior(textBox, expansionBehavior);
@@ -844,6 +876,7 @@
 
             setLogicalWidthForTextRun(lineBox, run, renderText, totalLogicalWidth, lineInfo, textBoxDataMap, verticalPositionCache, wordMeasurements);
         } else {
+            canHangPunctuationAtStart = false;
             bool encounteredJustifiedRuby = false;
             if (is<RenderRubyRun>(run->renderer()) && textAlign == JUSTIFY && run != trailingSpaceRun && downcast<RenderRubyRun>(run->renderer()).rubyBase()) {
                 auto* rubyBase = downcast<RenderRubyRun>(run->renderer()).rubyBase();

Modified: trunk/Source/WebCore/rendering/RenderText.cpp (197463 => 197464)


--- trunk/Source/WebCore/rendering/RenderText.cpp	2016-03-02 21:56:47 UTC (rev 197463)
+++ trunk/Source/WebCore/rendering/RenderText.cpp	2016-03-02 22:29:26 UTC (rev 197464)
@@ -502,6 +502,32 @@
     return f.width(run, fallbackFonts, glyphOverflow);
 }
 
+inline bool isHangablePunctuationAtLineStart(UChar c)
+{
+    return U_GET_GC_MASK(c) & (U_GC_PS_MASK | U_GC_PI_MASK | U_GC_PF_MASK);
+}
+
+inline bool isHangablePunctuationAtLineEnd(UChar c)
+{
+    return U_GET_GC_MASK(c) & (U_GC_PE_MASK | U_GC_PI_MASK | U_GC_PF_MASK);
+}
+
+float RenderText::hangablePunctuationStartWidth() const
+{
+    if (!textLength())
+        return 0;
+    
+    ASSERT(m_text);
+    StringImpl& text = *m_text.impl();
+    if (!isHangablePunctuationAtLineStart(text[0]))
+        return 0;
+    
+    const RenderStyle& style = this->style();
+    const FontCascade& font = style.fontCascade();
+        
+    return widthFromCache(font, 0, 1, 0, 0, 0, style);
+}
+    
 void RenderText::trimmedPrefWidths(float leadWidth,
                                    float& beginMinW, bool& beginWS,
                                    float& endMinW, bool& endWS,

Modified: trunk/Source/WebCore/rendering/RenderText.h (197463 => 197464)


--- trunk/Source/WebCore/rendering/RenderText.h	2016-03-02 21:56:47 UTC (rev 197463)
+++ trunk/Source/WebCore/rendering/RenderText.h	2016-03-02 22:29:26 UTC (rev 197464)
@@ -102,6 +102,7 @@
                            bool& hasBreakableChar, bool& hasBreak,
                            float& beginMaxW, float& endMaxW,
                            float& minW, float& maxW, bool& stripFrontSpaces);
+    float hangablePunctuationStartWidth() const;
 
     WEBCORE_EXPORT virtual IntRect linesBoundingBox() const;
     LayoutRect linesVisualOverflowBoundingBox() const;

Modified: trunk/Source/WebCore/rendering/SimpleLineLayout.cpp (197463 => 197464)


--- trunk/Source/WebCore/rendering/SimpleLineLayout.cpp	2016-03-02 21:56:47 UTC (rev 197463)
+++ trunk/Source/WebCore/rendering/SimpleLineLayout.cpp	2016-03-02 22:29:26 UTC (rev 197464)
@@ -111,7 +111,8 @@
     FlowHasNoParent                       = 1LLU  << 46,
     FlowHasNoChild                        = 1LLU  << 47,
     FlowChildIsSelected                   = 1LLU  << 48,
-    EndOfReasons                          = 1LLU  << 49
+    FlowHasHangingPunctuation             = 1LLU  << 49,
+    EndOfReasons                          = 1LLU  << 50
 };
 const unsigned NoReason = 0;
 
@@ -283,6 +284,9 @@
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasOutline, reasons, includeReasons);
     if (flow.isRubyText() || flow.isRubyBase())
         SET_REASON_AND_RETURN_IF_NEEDED(FlowIsRuby, reasons, includeReasons);
+    if (flow.style().hangingPunctuation() != NoHangingPunctuation)
+        SET_REASON_AND_RETURN_IF_NEEDED(FlowHasHangingPunctuation, reasons, includeReasons);
+    
     // Printing does pagination without a flow thread.
     if (flow.document().paginated())
         SET_REASON_AND_RETURN_IF_NEEDED(FlowIsPaginated, reasons, includeReasons);
@@ -798,6 +802,9 @@
     case FlowIsRuby:
         stream << "ruby";
         break;
+    case FlowHasHangingPunctuation:
+        stream << "hanging punctuation";
+        break;
     case FlowIsPaginated:
         stream << "paginated";
         break;

Modified: trunk/Source/WebCore/rendering/line/BreakingContext.h (197463 => 197464)


--- trunk/Source/WebCore/rendering/line/BreakingContext.h	2016-03-02 21:56:47 UTC (rev 197463)
+++ trunk/Source/WebCore/rendering/line/BreakingContext.h	2016-03-02 22:29:26 UTC (rev 197464)
@@ -737,7 +737,8 @@
     const FontCascade& font = style.fontCascade();
     bool isFixedPitch = font.isFixedPitch();
     bool canHyphenate = style.hyphens() == HyphensAuto && WebCore::canHyphenate(style.locale());
-
+    bool canHangPunctuationAtStart = style.hangingPunctuation() & FirstHangingPunctuation;
+    
     unsigned lastSpace = m_current.offset();
     float wordSpacing = m_currentStyle->fontCascade().wordSpacing();
     float lastSpaceWordSpacing = 0;
@@ -787,6 +788,9 @@
         UChar c = m_current.current();
         m_currentCharacterIsSpace = c == ' ' || c == '\t' || (!m_preservesNewline && (c == '\n'));
 
+        if (canHangPunctuationAtStart && !m_current.offset() && m_width.isFirstLine() && !m_width.committedWidth() && !wrapW && !m_current.offset())
+            m_width.addUncommittedWidth(-renderText.hangablePunctuationStartWidth());
+            
         if (!m_collapseWhiteSpace || !m_currentCharacterIsSpace)
             m_lineInfo.setEmpty(false, &m_block, &m_width);
 

Modified: trunk/Source/WebCore/rendering/line/LineWidth.h (197463 => 197464)


--- trunk/Source/WebCore/rendering/line/LineWidth.h	2016-03-02 21:56:47 UTC (rev 197463)
+++ trunk/Source/WebCore/rendering/line/LineWidth.h	2016-03-02 22:29:26 UTC (rev 197464)
@@ -77,6 +77,8 @@
     void fitBelowFloats(bool isFirstLine = false);
     void setTrailingWhitespaceWidth(float collapsedWhitespace, float borderPaddingMargin = 0);
     IndentTextOrNot shouldIndentText() const { return m_shouldIndentText; }
+    
+    bool isFirstLine() const { return m_isFirstLine; }
 
 private:
     void computeAvailableWidthFromLeftAndRight();
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to