Index: third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h |
diff --git a/third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h b/third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h |
index 7b5d2a0a98770914df9efc22daa3aa66bbd6a2a1..7e1eeb5ca803c765f862772fecab5f315f1a2d7e 100644 |
--- a/third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h |
+++ b/third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h |
@@ -40,6 +40,7 @@ |
#include "core/layout/line/TrailingObjects.h" |
#include "core/layout/line/WordMeasurement.h" |
#include "core/paint/PaintLayer.h" |
+#include "platform/text/Hyphenation.h" |
#include "platform/text/TextBreakIterator.h" |
#include "wtf/Allocator.h" |
#include "wtf/Vector.h" |
@@ -99,7 +100,7 @@ public: |
void handleReplaced(); |
bool handleText(WordMeasurements&, bool& hyphenated); |
void prepareForNextCharacter(const LineLayoutText&, bool& prohibitBreakInside, bool previousCharacterIsSpace); |
- bool canBreakAtWhitespace(bool breakWords, WordMeasurement&, bool stoppedIgnoringSpaces, bool& hyphenated, float charWidth, float& hyphenWidth, bool betweenWords, bool midWordBreak, bool canBreakMidWord, bool previousCharacterIsSpace, float lastWidthMeasurement, const LineLayoutText&, const Font&, bool applyWordSpacing, float wordSpacing); |
+ bool canBreakAtWhitespace(bool breakWords, WordMeasurement&, bool stoppedIgnoringSpaces, float charWidth, bool& hyphenated, bool disableSoftHyphen, float& hyphenWidth, bool betweenWords, bool midWordBreak, bool canBreakMidWord, bool previousCharacterIsSpace, float lastWidthMeasurement, const LineLayoutText&, const Font&, bool applyWordSpacing, float wordSpacing); |
bool trailingSpaceExceedsAvailableWidth(bool canBreakMidWord, const LineLayoutText&, WordMeasurement&, bool applyWordSpacing, bool wordSpacing, const Font&); |
WordMeasurement& calculateWordWidth(WordMeasurements&, LineLayoutText&, unsigned lastSpace, float& lastWidthMeasurement, float wordSpacingForWordMeasurement, const Font&, float wordTrailingSpaceWidth, UChar); |
void stopIgnoringSpaces(unsigned& lastSpace); |
@@ -117,6 +118,7 @@ private: |
bool rewindToMidWordBreak(WordMeasurement&, int end, float width); |
bool rewindToFirstMidWordBreak(LineLayoutText, const ComputedStyle&, const Font&, bool breakAll, WordMeasurement&); |
bool rewindToMidWordBreak(LineLayoutText, const ComputedStyle&, const Font&, bool breakAll, WordMeasurement&); |
+ bool hyphenate(LineLayoutText, const ComputedStyle&, const Font&, const Hyphenation&, float lastSpaceWordSpacing, WordMeasurement&); |
InlineBidiResolver& m_resolver; |
@@ -560,8 +562,8 @@ ALWAYS_INLINE bool BreakingContext::rewindToMidWordBreak( |
wordMeasurement.endOffset = end; |
wordMeasurement.width = width; |
- m_current.moveTo(m_current.getLineLayoutItem(), end); |
- m_lineBreak.moveTo(m_current.getLineLayoutItem(), m_current.offset()); |
+ m_current.moveTo(m_current.getLineLayoutItem(), end, m_current.nextBreakablePosition()); |
+ m_lineBreak.moveTo(m_current.getLineLayoutItem(), end, m_current.nextBreakablePosition()); |
return true; |
} |
@@ -622,6 +624,46 @@ ALWAYS_INLINE bool BreakingContext::rewindToMidWordBreak(LineLayoutText text, |
return rewindToMidWordBreak(wordMeasurement, end, rect.width()); |
} |
+ALWAYS_INLINE bool BreakingContext::hyphenate(LineLayoutText text, |
+ const ComputedStyle& style, const Font& font, |
+ const Hyphenation& hyphenation, float lastSpaceWordSpacing, |
+ WordMeasurement& wordMeasurement) |
+{ |
+ const unsigned minimumPrefixLength = 2; |
+ const unsigned minimumSuffixLength = 2; |
+ unsigned start = wordMeasurement.startOffset; |
+ unsigned len = wordMeasurement.endOffset - start; |
+ if (len <= minimumSuffixLength) |
+ return false; |
+ |
+ float hyphenWidth = text.hyphenWidth(font, textDirectionFromUnicode(m_resolver.position().direction())); |
+ float maxPrefixWidth = m_width.availableWidth() + LayoutUnit::epsilon() |
eae
2016/05/16 16:43:26
Do we need epsilon here? If we can ceil a floor to
kojii
2016/05/16 17:29:34
When tests use ch/em that should exactly fit, this
eae
2016/05/16 17:32:38
Thanks. We're trying to remove epsilon from LineWi
|
+ - m_width.currentWidth() - hyphenWidth - lastSpaceWordSpacing; |
+ |
+ // If the maximum width available for the prefix before the hyphen is small, then it is very unlikely |
+ // that an hyphenation opportunity exists, so do not bother to look for it. |
+ const int minPrefixWidthNumerator = 5; |
eae
2016/05/16 16:43:25
Where did you get these numbers from?
kojii
2016/05/16 17:29:34
From our removal patch (crrev.com/20526006). ToT W
eae
2016/05/16 17:32:38
Ok, could you add a quick comment explaining the n
|
+ const int minPrefixWidthDenominator = 4; |
+ if (maxPrefixWidth <= font.getFontDescription().computedSize() * minPrefixWidthNumerator / minPrefixWidthDenominator) |
+ return false; |
+ |
+ TextRun run = constructTextRun(font, text, start, len, style); |
+ run.setTabSize(!m_collapseWhiteSpace, style.getTabSize()); |
+ run.setXPos(m_width.currentWidth()); |
+ unsigned maxPrefixLength = font.offsetForPosition(run, maxPrefixWidth, false); |
+ if (maxPrefixLength < minimumPrefixLength) |
+ return false; |
+ |
+ unsigned prefixLength = hyphenation.lastHyphenLocation( |
+ text.text().createView(start, len), |
+ std::min(maxPrefixLength, len - minimumSuffixLength) + 1); |
+ if (!prefixLength || prefixLength < minimumPrefixLength) |
+ return false; |
+ |
+ FloatRect rect = font.selectionRectForText(run, FloatPoint(), 0, 0, prefixLength); |
eae
2016/05/16 16:43:25
Calling shaper.getCharacterRange instead of select
kojii
2016/05/16 17:29:34
Done.
eae
2016/05/16 17:32:38
Thanks!`
|
+ return rewindToMidWordBreak(wordMeasurement, start + prefixLength, rect.width()); |
+} |
+ |
inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements, bool& hyphenated) |
{ |
if (!m_current.offset()) |
@@ -660,6 +702,9 @@ inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements, bool |
// See: fast/css3-text/css3-word-break/word-break-all-wrap-with-floats.html |
float widthMeasurementAtLastBreakOpportunity = 0; |
+ Hyphenation* hyphenation = style.getHyphens() == HyphensAuto |
+ ? Hyphenation::get(font.getFontDescription().locale()) : nullptr; |
+ bool disableSoftHyphen = style.getHyphens() == HyphensNone; |
float hyphenWidth = 0; |
if (layoutText.isSVGInlineText()) { |
@@ -705,7 +750,7 @@ inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements, bool |
m_width.setTrailingWhitespaceWidth(0); |
} |
- if (c == softHyphenCharacter && m_autoWrap && !hyphenWidth) { |
+ if (c == softHyphenCharacter && m_autoWrap && !hyphenWidth && !disableSoftHyphen) { |
hyphenWidth = layoutText.hyphenWidth(font, textDirectionFromUnicode(m_resolver.position().direction())); |
m_width.addUncommittedWidth(hyphenWidth); |
} |
@@ -726,7 +771,8 @@ inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements, bool |
// Determine if we are in the whitespace between words. |
int nextBreakablePosition = m_current.nextBreakablePosition(); |
- bool betweenWords = c == newlineCharacter || (m_currWS != PRE && !m_atStart && m_layoutTextInfo.m_lineBreakIterator.isBreakable(m_current.offset(), nextBreakablePosition, lineBreakType)); |
+ bool betweenWords = c == newlineCharacter || (m_currWS != PRE && !m_atStart && m_layoutTextInfo.m_lineBreakIterator.isBreakable(m_current.offset(), nextBreakablePosition, lineBreakType) |
+ && (!disableSoftHyphen || m_current.previousInSameNode() != softHyphenCharacter)); |
m_current.setNextBreakablePosition(nextBreakablePosition); |
// If we're in the middle of a word or at the start of a new one and can't break there, then continue to the next character. |
@@ -788,18 +834,31 @@ inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements, bool |
m_width.fitBelowFloats(m_lineInfo.isFirstLine()); |
midWordBreak = false; |
- if (canBreakMidWord && !m_width.fitsOnLine()) { |
- m_width.addUncommittedWidth(-wordMeasurement.width); |
- if (rewindToMidWordBreak(layoutText, style, font, breakAll, wordMeasurement)) { |
- lastWidthMeasurement = wordMeasurement.width + lastSpaceWordSpacing; |
- midWordBreak = true; |
+ if (!m_width.fitsOnLine()) { |
+ if (canBreakMidWord) { |
+ m_width.addUncommittedWidth(-wordMeasurement.width); |
+ if (rewindToMidWordBreak(layoutText, style, font, breakAll, wordMeasurement)) { |
+ lastWidthMeasurement = wordMeasurement.width + lastSpaceWordSpacing; |
+ midWordBreak = true; |
+ } |
+ m_width.addUncommittedWidth(wordMeasurement.width); |
+ } else if (hyphenation) { |
+ m_width.addUncommittedWidth(-wordMeasurement.width); |
+ DCHECK(lastSpace == static_cast<unsigned>(wordMeasurement.startOffset)); |
+ DCHECK(m_current.offset() == static_cast<unsigned>(wordMeasurement.endOffset)); |
+ if (hyphenate(layoutText, style, font, *hyphenation, lastSpaceWordSpacing, wordMeasurement)) { |
+ m_width.addUncommittedWidth(wordMeasurement.width); |
+ hyphenated = true; |
+ m_atEnd = true; |
+ return false; |
+ } |
+ m_width.addUncommittedWidth(wordMeasurement.width); |
} |
- m_width.addUncommittedWidth(wordMeasurement.width); |
} |
// If there is a soft-break available at this whitespace position then take it. |
applyWordSpacing = wordSpacing && m_currentCharacterIsSpace; |
- if (canBreakAtWhitespace(breakWords, wordMeasurement, stoppedIgnoringSpaces, hyphenated, charWidth, hyphenWidth, betweenWords, midWordBreak, canBreakMidWord, previousCharacterIsSpace, lastWidthMeasurement, layoutText, font, applyWordSpacing, wordSpacing)) |
+ if (canBreakAtWhitespace(breakWords, wordMeasurement, stoppedIgnoringSpaces, charWidth, hyphenated, disableSoftHyphen, hyphenWidth, betweenWords, midWordBreak, canBreakMidWord, previousCharacterIsSpace, lastWidthMeasurement, layoutText, font, applyWordSpacing, wordSpacing)) |
return false; |
// If there is a hard-break available at this whitespace position then take it. |
@@ -882,10 +941,23 @@ inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements, bool |
if (midWordBreak) { |
m_width.commit(); |
m_atEnd = true; |
- } else if (!m_width.fitsOnLine() && !hyphenated |
- && m_lineBreak.previousInSameNode() == softHyphenCharacter) { |
- hyphenated = true; |
- m_atEnd = true; |
+ } else if (!m_width.fitsOnLine()) { |
+ if (hyphenation && (m_nextObject || m_lineInfo.isEmpty())) { |
+ m_width.addUncommittedWidth(-wordMeasurement.width); |
+ DCHECK(lastSpace == static_cast<unsigned>(wordMeasurement.startOffset)); |
+ DCHECK(m_current.offset() == static_cast<unsigned>(wordMeasurement.endOffset)); |
+ if (hyphenate(layoutText, style, font, *hyphenation, lastSpaceWordSpacing, wordMeasurement)) { |
+ hyphenated = true; |
+ m_atEnd = true; |
+ } |
+ m_width.addUncommittedWidth(wordMeasurement.width); |
+ } |
+ if (!hyphenated |
+ && m_lineBreak.previousInSameNode() == softHyphenCharacter |
+ && !disableSoftHyphen) { |
+ hyphenated = true; |
+ m_atEnd = true; |
+ } |
} |
return false; |
} |
@@ -961,7 +1033,7 @@ inline bool BreakingContext::trailingSpaceExceedsAvailableWidth(bool canBreakMid |
return false; |
} |
-inline bool BreakingContext::canBreakAtWhitespace(bool breakWords, WordMeasurement& wordMeasurement, bool stoppedIgnoringSpaces, bool& hyphenated, float charWidth, float& hyphenWidth, bool betweenWords, bool midWordBreak, bool canBreakMidWord, bool previousCharacterIsSpace, float lastWidthMeasurement, const LineLayoutText& layoutText, const Font& font, bool applyWordSpacing, float wordSpacing) |
+inline bool BreakingContext::canBreakAtWhitespace(bool breakWords, WordMeasurement& wordMeasurement, bool stoppedIgnoringSpaces, float charWidth, bool& hyphenated, bool disableSoftHyphen, float& hyphenWidth, bool betweenWords, bool midWordBreak, bool canBreakMidWord, bool previousCharacterIsSpace, float lastWidthMeasurement, const LineLayoutText& layoutText, const Font& font, bool applyWordSpacing, float wordSpacing) |
{ |
if (!m_autoWrap && !breakWords) |
return false; |
@@ -978,7 +1050,7 @@ inline bool BreakingContext::canBreakAtWhitespace(bool breakWords, WordMeasureme |
m_lineInfo.setPreviousLineBrokeCleanly(true); |
wordMeasurement.endOffset = m_lineBreak.offset(); |
} |
- if (m_lineBreak.getLineLayoutItem() && m_lineBreak.offset() && m_lineBreak.getLineLayoutItem().isText() && LineLayoutText(m_lineBreak.getLineLayoutItem()).textLength() && LineLayoutText(m_lineBreak.getLineLayoutItem()).characterAt(m_lineBreak.offset() - 1) == softHyphenCharacter) |
+ if (m_lineBreak.getLineLayoutItem() && m_lineBreak.offset() && m_lineBreak.getLineLayoutItem().isText() && LineLayoutText(m_lineBreak.getLineLayoutItem()).textLength() && LineLayoutText(m_lineBreak.getLineLayoutItem()).characterAt(m_lineBreak.offset() - 1) == softHyphenCharacter && !disableSoftHyphen) |
hyphenated = true; |
if (m_lineBreak.offset() && m_lineBreak.offset() != (unsigned)wordMeasurement.endOffset && !wordMeasurement.width) { |
if (charWidth) { |