WebCore: Improved the handling of soft hyphens in Copy and Find operations, addressing the following
bugs:
Reviewed by Darin Adler.
<rdar://problem/7938878> Soft hyphenation and the clipboard
https://bugs.webkit.org/show_bug.cgi?id=11154
window.getSelection().toString() breaks soft hyphen ­
https://bugs.webkit.org/show_bug.cgi?id=26774
<rdar://problem/5640505> soft hyphen breaks search function
https://bugs.webkit.org/show_bug.cgi?id=16675
Test: fast/text/find-soft-hyphen.html
Changed line layout code to not skip over soft hyphens but rather include them in the text
boxes. Changed font code to render the soft hyphen character as a zero width space, so that
the former change will not affect rendering of soft hyphens in the middle of the line. Also
changed line layout code to mark text boxes where hyphenation occurred due to a soft hyphen
as hyphenated, so that the hyphen string specified in CSS will be appended to them.
Not omitting the soft hyphens from the text boxes makes the text iterator emit them, which
solves the Copy and plain-text conversion issues. Previously, the iterator would emit a space
to account for non-rendered characters between adjacent boxes, which was wrong in this case.
To make Find work, soft hyphens are folded into 0, which is ignorable in the collation used
for Find.
* editing/TextIterator.cpp:
(WebCore::foldQuoteMarkOrSoftHyphen): Renamed foldQuoteMark() to this and added folding of
soft hyphen to 0.
(WebCore::foldQuoteMarksAndSoftHyphens): Renamed foldQuoteMarks() to thid and added folding
of soft hyphen to 0.
(WebCore::SearchBuffer::SearchBuffer): Updated for renames.
(WebCore::SearchBuffer::append): Ditto.
* platform/graphics/Font.h:
(WebCore::Font::treatAsSpace): Replaced number literal with name.
(WebCore::Font::treatAsZeroWidthSpace): Added softHyphen.
* platform/graphics/GlyphPageTreeNode.cpp:
(WebCore::GlyphPageTreeNode::initializePage): Get the zero width space glyph for soft hyphen.
* platform/graphics/mac/ComplexTextController.cpp:
(WebCore::ComplexTextController::collectComplexTextRuns): Removed special handling that made
a trailing soft hyphen render as hyphen-minus. All soft hyphens are now rendered as zero width
spaces, and where a line break actually occurs at a soft hyphen, rendering code appends the
CSS-specified hyphenate character to the text run that is passed to us here.
* rendering/RenderBlockLineLayout.cpp:
(WebCore::chopMidpointsAt): Removed this function, which was only used for skipping over soft
hyphens.
(WebCore::checkMidpoints): Removed code related to skipping over soft hyphens.
(WebCore::RenderBlock::findNextLineBreak): Removed code to skip over soft hyphens. Ignore
a line break opportunity at a soft hyphen if the style specifies 'hyphens: none'. Set
'hyphenated' to true if a line break occurs at a soft hyphen.
* rendering/style/RenderStyle.cpp:
(WebCore::RenderStyle::hyphenString): Changed the assertion to allow querying for the hyphen
string for 'hyphens: manual'.
LayoutTests: Improved the handling of soft hyphens in copy and find operations, addressing the following
bugs:
Reviewed by Darin Adler.
<rdar://problem/7938878> Soft hyphenation and the clipboard
https://bugs.webkit.org/show_bug.cgi?id=11154
window.getSelection().toString() breaks soft hyphen ­
https://bugs.webkit.org/show_bug.cgi?id=26774
<rdar://problem/5640505> soft hyphen breaks search function
https://bugs.webkit.org/show_bug.cgi?id=16675
* fast/text/find-soft-hyphen-expected.txt: Added.
* fast/text/find-soft-hyphen.html: Added.
* fast/text/script-tests/find-soft-hyphen.js: Added.
(canFind):
* platform/mac/fast/text/basic/014-expected.checksum:
* platform/mac/fast/text/basic/014-expected.png:
* platform/mac/fast/text/basic/014-expected.txt:
* platform/mac/fast/text/capitalize-boundaries-expected.checksum:
* platform/mac/fast/text/capitalize-boundaries-expected.png:
* platform/mac/fast/text/capitalize-boundaries-expected.txt:
* platform/mac/fast/text/hyphens-expected.checksum:
* platform/mac/fast/text/hyphens-expected.png:
* platform/mac/fast/text/hyphens-expected.txt:
* platform/mac/fast/text/midword-break-after-breakable-char-expected.checksum:
* platform/mac/fast/text/midword-break-after-breakable-char-expected.png:
* platform/mac/fast/text/midword-break-after-breakable-char-expected.txt:
* platform/mac/fast/text/soft-hyphen-2-expected.png:
* platform/mac/fast/text/soft-hyphen-2-expected.txt:
* platform/mac/fast/text/soft-hyphen-3-expected.png:
* platform/mac/fast/text/soft-hyphen-3-expected.txt:
* platform/mac/fast/text/softHyphen-expected.checksum:
* platform/mac/fast/text/softHyphen-expected.png:
* platform/mac/fast/text/softHyphen-expected.txt:
* platform/mac/fast/text/word-break-soft-hyphen-expected.checksum:
* platform/mac/fast/text/word-break-soft-hyphen-expected.png:
* platform/mac/fast/text/word-break-soft-hyphen-expected.txt:
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@68551 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/WebCore/rendering/RenderBlockLineLayout.cpp b/WebCore/rendering/RenderBlockLineLayout.cpp
index 30b0cac..81a15eb 100644
--- a/WebCore/rendering/RenderBlockLineLayout.cpp
+++ b/WebCore/rendering/RenderBlockLineLayout.cpp
@@ -79,20 +79,6 @@
return extraWidth;
}
-static void chopMidpointsAt(LineMidpointState& lineMidpointState, RenderObject* obj, unsigned pos)
-{
- if (!lineMidpointState.numMidpoints)
- return;
- InlineIterator* midpoints = lineMidpointState.midpoints.data();
- for (int i = lineMidpointState.numMidpoints - 1; i >= 0; i--) {
- const InlineIterator& point = midpoints[i];
- if (point.obj == obj && point.pos == pos) {
- lineMidpointState.numMidpoints = i;
- break;
- }
- }
-}
-
static void checkMidpoints(LineMidpointState& lineMidpointState, InlineIterator& lBreak)
{
// Check to see if our last midpoint is a start point beyond the line break. If so,
@@ -108,21 +94,8 @@
if (currpoint == lBreak) {
// We hit the line break before the start point. Shave off the start point.
lineMidpointState.numMidpoints--;
- if (endpoint.obj->style()->collapseWhiteSpace()) {
- if (endpoint.obj->isText()) {
- // Don't shave a character off the endpoint if it was from a soft hyphen.
- RenderText* textObj = toRenderText(endpoint.obj);
- if (endpoint.pos + 1 < textObj->textLength()) {
- if (textObj->characters()[endpoint.pos+1] == softHyphen)
- return;
- } else if (startpoint.obj->isText()) {
- RenderText *startText = toRenderText(startpoint.obj);
- if (startText->textLength() && startText->characters()[0] == softHyphen)
- return;
- }
- }
+ if (endpoint.obj->style()->collapseWhiteSpace())
endpoint.pos--;
- }
}
}
}
@@ -1655,6 +1628,7 @@
bool breakWords = o->style()->breakWords() && ((autoWrap && !w) || currWS == PRE);
bool midWordBreak = false;
bool breakAll = o->style()->wordBreak() == BreakAllWordBreak && autoWrap;
+ int hyphenWidth = 0;
if (t->isWordBreak()) {
w += tmpW;
@@ -1673,48 +1647,13 @@
if (!collapseWhiteSpace || !currentCharacterIsSpace)
isLineEmpty = false;
-
- // Check for soft hyphens. Go ahead and ignore them.
- if (c == softHyphen) {
- if (!ignoringSpaces) {
- // Ignore soft hyphens
- InlineIterator beforeSoftHyphen;
- if (pos)
- beforeSoftHyphen = InlineIterator(0, o, pos - 1);
- else
- beforeSoftHyphen = InlineIterator(0, last, last->isText() ? toRenderText(last)->textLength() - 1 : 0);
- // Two consecutive soft hyphens. Avoid overlapping midpoints.
- if (lineMidpointState.numMidpoints && lineMidpointState.midpoints[lineMidpointState.numMidpoints - 1].obj == o &&
- lineMidpointState.midpoints[lineMidpointState.numMidpoints - 1].pos == pos)
- lineMidpointState.numMidpoints--;
- else
- addMidpoint(lineMidpointState, beforeSoftHyphen);
- // Add the width up to but not including the hyphen.
- tmpW += textWidth(t, lastSpace, pos - lastSpace, f, w + tmpW, isFixedPitch, collapseWhiteSpace) + lastSpaceWordSpacing;
-
- // For wrapping text only, include the hyphen. We need to ensure it will fit
- // on the line if it shows when we break.
- if (autoWrap)
- tmpW += textWidth(t, pos, 1, f, w + tmpW, isFixedPitch, collapseWhiteSpace);
-
- InlineIterator afterSoftHyphen(0, o, pos);
- afterSoftHyphen.increment();
- addMidpoint(lineMidpointState, afterSoftHyphen);
- }
-
- pos++;
- len--;
- lastSpaceWordSpacing = 0;
- lastSpace = pos; // Cheesy hack to prevent adding in widths of the run twice.
- if (style->hyphens() == HyphensNone) {
- // Prevent a line break at the soft hyphen by ensuring that betweenWords is false
- // in the next iteration.
- atStart = true;
- }
- continue;
+ if (c == softHyphen && autoWrap && !hyphenWidth && style->hyphens() != HyphensNone) {
+ const AtomicString& hyphenString = style->hyphenString();
+ hyphenWidth = f.width(TextRun(hyphenString.characters(), hyphenString.length()));
+ tmpW += hyphenWidth;
}
-
+
#if ENABLE(SVG)
if (isSVGText) {
RenderSVGInlineText* svgInlineText = static_cast<RenderSVGInlineText*>(t);
@@ -1737,8 +1676,8 @@
midWordBreak = w + wrapW + charWidth > width;
}
- bool betweenWords = c == '\n' || (currWS != PRE && !atStart && isBreakable(str, pos, strlen, nextBreakable, breakNBSP));
-
+ bool betweenWords = c == '\n' || (currWS != PRE && !atStart && isBreakable(str, pos, strlen, nextBreakable, breakNBSP) && (style->hyphens() != HyphensNone || (pos && str[pos - 1] != softHyphen)));
+
if (betweenWords || midWordBreak) {
bool stoppedIgnoringSpaces = false;
if (ignoringSpaces) {
@@ -1808,13 +1747,17 @@
lBreak.increment();
previousLineBrokeCleanly = true;
}
+ if (lBreak.obj && lBreak.pos && lBreak.obj->isText() && toRenderText(lBreak.obj)->textLength() && toRenderText(lBreak.obj)->characters()[lBreak.pos - 1] == softHyphen && style->hyphens() != HyphensNone)
+ hyphenated = true;
goto end; // Didn't fit. Jump to the end.
} else {
if (!betweenWords || (midWordBreak && !autoWrap))
tmpW -= additionalTmpW;
- if (pos > 0 && str[pos-1] == softHyphen)
+ if (hyphenWidth) {
// Subtract the width of the soft hyphen out since we fit on a line.
- tmpW -= textWidth(t, pos - 1, 1, f, w + tmpW, isFixedPitch, collapseWhiteSpace);
+ tmpW -= hyphenWidth;
+ hyphenWidth = 0;
+ }
}
}
@@ -2059,14 +2002,6 @@
lBreak.increment();
}
- if (lBreak.obj && lBreak.pos >= 2 && lBreak.obj->isText()) {
- // For soft hyphens on line breaks, we have to chop out the midpoints that made us
- // ignore the hyphen so that it will render at the end of the line.
- UChar c = toRenderText(lBreak.obj)->characters()[lBreak.pos - 1];
- if (c == softHyphen)
- chopMidpointsAt(lineMidpointState, lBreak.obj, lBreak.pos - 2);
- }
-
return lBreak;
}