Changeset 211108 in webkit
- Timestamp:
- Jan 24, 2017 1:38:20 PM (7 years ago)
- Location:
- trunk
- Files:
-
- 4 added
- 12 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r211096 r211108 1 2017-01-24 Zalan Bujtas <zalan@apple.com> 2 3 Simple line layout: Add support for hyphen: auto. 4 https://bugs.webkit.org/show_bug.cgi?id=167297 5 <rdar://problem/30119463> 6 7 Reviewed by Antti Koivisto. 8 9 * fast/text/simple-line-hyphens-with-text-align-expected.html: Added. 10 * fast/text/simple-line-hyphens-with-text-align.html: Added. 11 * fast/text/simple-line-hyphens-with-word-letter-spacing-expected.html: Added. 12 * fast/text/simple-line-hyphens-with-word-letter-spacing.html: Added. 13 1 14 2017-01-24 Ryan Haddad <ryanhaddad@apple.com> 2 15 -
trunk/Source/WebCore/ChangeLog
r211097 r211108 1 2017-01-24 Zalan Bujtas <zalan@apple.com> 2 3 Simple line layout: Add support for hyphen: auto. 4 https://bugs.webkit.org/show_bug.cgi?id=167297 5 <rdar://problem/30119463> 6 7 Reviewed by Antti Koivisto. 8 9 Implement hyphen: auto for simple line layout. 10 11 Tests: fast/text/simple-line-hyphens-with-text-align.html 12 fast/text/simple-line-hyphens-with-word-letter-spacing.html 13 14 * platform/text/Hyphenation.h: 15 (WebCore::enoughWidthForHyphenation): 16 * rendering/RenderTreeAsText.cpp: 17 (WebCore::writeSimpleLine): 18 (WebCore::write): 19 * rendering/SimpleLineLayout.cpp: 20 (WebCore::SimpleLineLayout::canUseForStyle): 21 (WebCore::SimpleLineLayout::LineState::appendFragmentAndCreateRunIfNeeded): Inherit the hyphen attribute from the 22 run-to-be-appended. Ensure that we don't append additional runs when the last run has hyphen. 23 (WebCore::SimpleLineLayout::splitFragmentToFitLine): Before calling into the lastHyphenPosition() we need to 24 ensure that the hyphen would surely fit (even on the splitting position). 25 (WebCore::SimpleLineLayout::createLineRuns): Probe hypenation for overhanging non-whitespace runs. 26 (WebCore::SimpleLineLayout::printReason): 27 * rendering/SimpleLineLayout.h: 28 (WebCore::SimpleLineLayout::Run::Run): 29 * rendering/SimpleLineLayoutFlowContents.h: 30 (WebCore::SimpleLineLayout::FlowContents::Segment::toSegmentPosition): 31 (WebCore::SimpleLineLayout::FlowContents::Segment::toRenderPosition): 32 * rendering/SimpleLineLayoutResolver.cpp: 33 (WebCore::SimpleLineLayout::RunResolver::Run::Run): 34 (WebCore::SimpleLineLayout::RunResolver::Run::constructStringForHyphenIfNeeded): 35 (WebCore::SimpleLineLayout::RunResolver::Run::text): 36 * rendering/SimpleLineLayoutResolver.h: 37 (WebCore::SimpleLineLayout::RunResolver::Run::hasHyphen): 38 * rendering/SimpleLineLayoutTextFragmentIterator.cpp: 39 (WebCore::SimpleLineLayout::TextFragmentIterator::Style::Style): 40 (WebCore::SimpleLineLayout::TextFragmentIterator::nextBreakablePosition): 41 (WebCore::SimpleLineLayout::TextFragmentIterator::nextNonWhitespacePosition): 42 (WebCore::SimpleLineLayout::TextFragmentIterator::textWidth): 43 (WebCore::SimpleLineLayout::TextFragmentIterator::lastHyphenPosition): We only check the actual run for hyphenation ignoring 44 the neighboring runs. This might need to be changed in the future. 45 (WebCore::SimpleLineLayout::TextFragmentIterator::runWidth): 46 * rendering/SimpleLineLayoutTextFragmentIterator.h: 47 (WebCore::SimpleLineLayout::TextFragmentIterator::TextFragment::TextFragment): 48 (WebCore::SimpleLineLayout::TextFragmentIterator::TextFragment::hasHyphen): 49 (WebCore::SimpleLineLayout::TextFragmentIterator::TextFragment::operator==): 50 (WebCore::SimpleLineLayout::TextFragmentIterator::TextFragment::split): 51 (WebCore::SimpleLineLayout::TextFragmentIterator::TextFragment::splitWithHyphen): 52 * rendering/line/BreakingContext.h: 53 (WebCore::tryHyphenating): 54 1 55 2017-01-24 Matt Rajca <mrajca@apple.com> 2 56 -
trunk/Source/WebCore/platform/text/Hyphenation.h
r165848 r211108 32 32 namespace WebCore { 33 33 34 inline static bool enoughWidthForHyphenation(float availableWidth, float fontPixelSize) 35 { 36 // If the maximum width available for the prefix before the hyphen is small, then it is very unlikely 37 // that an hyphenation opportunity exists, so do not bother to look for it. 38 return availableWidth > fontPixelSize * 5 / 4; 39 40 } 34 41 bool canHyphenate(const AtomicString& localeIdentifier); 35 42 size_t lastHyphenLocation(StringView, size_t beforeIndex, const AtomicString& localeIdentifier); -
trunk/Source/WebCore/rendering/RenderTreeAsText.cpp
r210763 r211108 494 494 } 495 495 496 static void writeSimpleLine(TextStream& ts, const RenderText& o, const FloatRect& rect, StringView text) 497 { 496 static void writeSimpleLine(TextStream& ts, const RenderText& renderText, const SimpleLineLayout::RunResolver::Run& run) 497 { 498 auto rect = run.rect(); 498 499 int x = rect.x(); 499 500 int y = rect.y(); 500 501 int logicalWidth = ceilf(rect.x() + rect.width()) - x; 501 502 502 if (is<RenderTableCell>(* o.containingBlock()))503 y -= floorToInt(downcast<RenderTableCell>(* o.containingBlock()).intrinsicPaddingBefore());504 503 if (is<RenderTableCell>(*renderText.containingBlock())) 504 y -= floorToInt(downcast<RenderTableCell>(*renderText.containingBlock()).intrinsicPaddingBefore()); 505 505 506 ts << "text run at (" << x << "," << y << ") width " << logicalWidth; 506 ts << ": " 507 << quoteAndEscapeNonPrintables(text); 507 if (run.hasHyphen()) { 508 ts << ": " << quoteAndEscapeNonPrintables(run.text().substring(0, run.text().length() - 1)); 509 ts << " + hyphen string " << quoteAndEscapeNonPrintables(renderText.style().hyphenString().string()); 510 } else 511 ts << ": " << quoteAndEscapeNonPrintables(run.text()); 512 508 513 ts << "\n"; 509 514 } … … 556 561 for (const auto& run : resolver.rangeForRenderer(text)) { 557 562 writeIndent(ts, indent + 1); 558 writeSimpleLine(ts, text, run .rect(), run.text());563 writeSimpleLine(ts, text, run); 559 564 } 560 565 } else { -
trunk/Source/WebCore/rendering/SimpleLineLayout.cpp
r210985 r211108 34 34 #include "HitTestRequest.h" 35 35 #include "HitTestResult.h" 36 #include "Hyphenation.h" 36 37 #include "InlineTextBox.h" 37 38 #include "LineWidth.h" … … 86 87 FlowHasLineAlignEdges = 1LLU << 21, 87 88 FlowHasLineSnap = 1LLU << 22, 88 FlowHasHypens Auto= 1LLU << 23,89 FlowHasHypensLimit = 1LLU << 23, 89 90 FlowHasTextEmphasisFillOrMark = 1LLU << 24, 90 91 FlowHasTextShadow = 1LLU << 25, … … 237 238 if (style.lineSnap() != LineSnapNone) 238 239 SET_REASON_AND_RETURN_IF_NEEDED(FlowHasLineSnap, reasons, includeReasons); 239 if (style.hyphens() == HyphensAuto) 240 SET_REASON_AND_RETURN_IF_NEEDED(FlowHasHypensAuto, reasons, includeReasons); 240 if (style.hyphenationLimitBefore() != RenderStyle::initialHyphenationLimitBefore() 241 || style.hyphenationLimitAfter() != RenderStyle::initialHyphenationLimitAfter() 242 || style.hyphenationLimitLines() != RenderStyle::initialHyphenationLimitLines()) 243 SET_REASON_AND_RETURN_IF_NEEDED(FlowHasHypensLimit, reasons, includeReasons); 241 244 if (style.textEmphasisFill() != TextEmphasisFillFilled || style.textEmphasisMark() != TextEmphasisMarkNone) 242 245 SET_REASON_AND_RETURN_IF_NEEDED(FlowHasTextEmphasisFillOrMark, reasons, includeReasons); … … 454 457 // New line needs new run. 455 458 if (!m_runsWidth) 456 runs.append(Run(fragment.start(), endPosition, m_runsWidth, m_runsWidth + fragment.width(), false ));459 runs.append(Run(fragment.start(), endPosition, m_runsWidth, m_runsWidth + fragment.width(), false, fragment.hasHyphen())); 457 460 else { 458 461 const auto& lastFragment = m_fragments.last(); … … 471 474 } 472 475 if (lastFragment.isLastInRenderer() || lastFragment.isCollapsed()) 473 runs.append(Run(fragment.start(), endPosition, m_runsWidth, m_runsWidth + fragment.width(), false ));476 runs.append(Run(fragment.start(), endPosition, m_runsWidth, m_runsWidth + fragment.width(), false, fragment.hasHyphen())); 474 477 else { 475 478 Run& lastRun = runs.last(); 476 479 lastRun.end = endPosition; 477 480 lastRun.logicalRight += fragment.width(); 481 ASSERT(!lastRun.hasHyphen); 482 lastRun.hasHyphen = fragment.hasHyphen(); 478 483 } 479 484 } … … 624 629 }); 625 630 unsigned splitPosition = (*it); 626 if (keepAtLeastOneCharacter && splitPosition == fragmentToSplit.start()) 627 ++splitPosition; 631 auto& style = textFragmentIterator.style(); 632 // Does first character fit this line? 633 if (splitPosition == fragmentToSplit.start()) { 634 if (keepAtLeastOneCharacter) 635 ++splitPosition; 636 } else if (style.shouldHyphenate && enoughWidthForHyphenation(availableWidth, style.font.pixelSize())) { 637 // We might be able to fit the hyphen at the split position. 638 auto splitPositionWithHyphen = splitPosition; 639 // Find a splitting position where hyphen surely fits. 640 auto leftSideWidth = textFragmentIterator.textWidth(start, splitPosition, 0); 641 while (leftSideWidth + style.hyphenStringWidth > availableWidth) { 642 if (--splitPositionWithHyphen <= start) 643 break; // No space for hyphen. 644 leftSideWidth -= textFragmentIterator.textWidth(splitPositionWithHyphen, splitPositionWithHyphen + 1, 0); 645 } 646 if (splitPositionWithHyphen > start) { 647 if (auto hyphenPosition = textFragmentIterator.lastHyphenPosition(fragmentToSplit, splitPositionWithHyphen + 1)) 648 return fragmentToSplit.splitWithHyphen(*hyphenPosition, textFragmentIterator); 649 } 650 } 628 651 return fragmentToSplit.split(splitPosition, textFragmentIterator); 629 652 } … … 737 760 break; 738 761 } 762 ASSERT(fragment.type() == TextFragmentIterator::TextFragment::NonWhitespace); 763 // Find out if this non-whitespace fragment has a hyphen where we can break. 764 if (style.shouldHyphenate) { 765 auto fragmentToSplit = fragment; 766 // Split and check if we actually ended up with a hyphen. 767 auto overflowFragment = splitFragmentToFitLine(fragmentToSplit, line.availableWidth() - line.width(), emptyLine, textFragmentIterator); 768 if (fragmentToSplit.hasHyphen()) { 769 line.setOverflowedFragment(overflowFragment); 770 line.appendFragmentAndCreateRunIfNeeded(fragmentToSplit, runs); 771 break; 772 } 773 // No hyphen, no split. 774 } 739 775 // Non-breakable non-whitespace first fragment. Add it to the current line. -it overflows though. 740 ASSERT(fragment.type() == TextFragmentIterator::TextFragment::NonWhitespace);741 776 if (emptyLine) { 742 777 forceFragmentToLine(line, textFragmentIterator, runs, fragment); … … 957 992 stream << "-webkit-line-snap property"; 958 993 break; 959 case FlowHasHypens Auto:960 stream << "hyphen : auto";994 case FlowHasHypensLimit: 995 stream << "hyphen-limit-* property"; 961 996 break; 962 997 case FlowHasTextEmphasisFillOrMark: -
trunk/Source/WebCore/rendering/SimpleLineLayout.h
r208668 r211108 47 47 Run() { } 48 48 #endif 49 Run(unsigned start, unsigned end, float logicalLeft, float logicalRight, bool isEndOfLine )50 : start(start)51 , end(end)49 Run(unsigned start, unsigned end, float logicalLeft, float logicalRight, bool isEndOfLine, bool hasHyphen) 50 : end(end) 51 , start(start) 52 52 , isEndOfLine(isEndOfLine) 53 , hasHyphen(hasHyphen) 53 54 , logicalLeft(logicalLeft) 54 55 , logicalRight(logicalRight) 55 56 { } 56 57 57 unsigned start;58 unsigned end : 31;58 unsigned end; 59 unsigned start : 30; 59 60 unsigned isEndOfLine : 1; 61 unsigned hasHyphen : 1; 60 62 float logicalLeft; 61 63 float logicalRight; -
trunk/Source/WebCore/rendering/SimpleLineLayoutFlowContents.h
r208668 r211108 38 38 39 39 struct Segment { 40 unsigned toSegmentPosition(unsigned position) const 41 { 42 ASSERT(position >= start); 43 return position - start; 44 } 45 unsigned toRenderPosition(unsigned position) const { return start + position; } 40 46 unsigned start; 41 47 unsigned end; -
trunk/Source/WebCore/rendering/SimpleLineLayoutResolver.cpp
r196561 r211108 48 48 : m_iterator(iterator) 49 49 { 50 constructStringForHyphenIfNeeded(); 51 } 52 53 void RunResolver::Run::constructStringForHyphenIfNeeded() 54 { 55 auto& run = m_iterator.simpleRun(); 56 if (!run.hasHyphen) 57 return; 58 // Empty runs should not have hyphen. 59 ASSERT(run.start < run.end); 60 auto& segment = m_iterator.resolver().m_flowContents.segmentForRun(run.start, run.end); 61 auto text = StringView(segment.text).substring(segment.toSegmentPosition(run.start), run.end - run.start); 62 m_textWithHyphen = makeString(text, m_iterator.resolver().flow().style().hyphenString()); 50 63 } 51 64 … … 70 83 StringView RunResolver::Run::text() const 71 84 { 85 if (m_textWithHyphen) 86 return StringView(*m_textWithHyphen); 72 87 auto& run = m_iterator.simpleRun(); 73 88 ASSERT(run.start < run.end); … … 75 90 // We currently split runs on segment boundaries (different RenderObject). 76 91 ASSERT(run.end <= segment.end); 77 return StringView(segment.text).substring( run.start - segment.start, run.end - run.start);92 return StringView(segment.text).substring(segment.toSegmentPosition(run.start), run.end - run.start); 78 93 } 79 94 -
trunk/Source/WebCore/rendering/SimpleLineLayoutResolver.h
r208668 r211108 66 66 StringView text() const; 67 67 bool isEndOfLine() const; 68 bool hasHyphen() const { return m_iterator.simpleRun().hasHyphen; } 68 69 69 70 unsigned lineIndex() const; … … 71 72 private: 72 73 float computeBaselinePosition() const; 74 void constructStringForHyphenIfNeeded(); 73 75 74 76 const Iterator& m_iterator; 77 std::optional<String> m_textWithHyphen; 75 78 }; 76 79 -
trunk/Source/WebCore/rendering/SimpleLineLayoutTextFragmentIterator.cpp
r210456 r211108 27 27 #include "SimpleLineLayoutTextFragmentIterator.h" 28 28 29 #include "Hyphenation.h" 29 30 #include "RenderBlockFlow.h" 30 31 #include "RenderChildIterator.h" … … 47 48 , wordSpacing(font.wordSpacing()) 48 49 , tabWidth(collapseWhitespace ? 0 : style.tabSize()) 50 , shouldHyphenate(style.hyphens() == HyphensAuto && canHyphenate(style.locale())) 51 , hyphenStringWidth(shouldHyphenate ? font.width(TextRun(style.hyphenString())) : 0) 49 52 , locale(style.locale()) 50 53 { … … 144 147 m_lineBreakIterator.resetStringAndReleaseIterator(segment.text, m_style.locale, LineBreakIteratorMode::Default); 145 148 } 146 unsigned segmentPosition = startPosition - segment.start; 147 return segment.start + nextBreakablePositionInSegment(m_lineBreakIterator, segmentPosition, m_style.breakNBSP, m_style.keepAllWordsForCJK); 149 return segment.toRenderPosition(nextBreakablePositionInSegment(m_lineBreakIterator, segment.toSegmentPosition(startPosition), m_style.breakNBSP, m_style.keepAllWordsForCJK)); 148 150 } 149 151 … … 155 157 unsigned position = startPosition; 156 158 for (; position < segment.end; ++position) { 157 auto character = text[ position - segment.start];159 auto character = text[segment.toSegmentPosition(position)]; 158 160 bool isWhitespace = character == ' ' || character == '\t' || (!m_style.preserveNewline && character == '\n'); 159 161 if (!isWhitespace) … … 171 173 return 0; 172 174 if (m_style.font.isFixedPitch() || (from == segment.start && to == segment.end)) 173 return downcast<RenderText>(segment.renderer).width( from - segment.start, to - from, m_style.font, xPosition, nullptr, nullptr);175 return downcast<RenderText>(segment.renderer).width(segment.toSegmentPosition(from), to - from, m_style.font, xPosition, nullptr, nullptr); 174 176 return runWidth(segment, from, to, xPosition); 177 } 178 179 std::optional<unsigned> TextFragmentIterator::lastHyphenPosition(const TextFragmentIterator::TextFragment& run, unsigned beforeIndex) const 180 { 181 ASSERT(run.start() < beforeIndex); 182 auto& segment = *m_currentSegment; 183 ASSERT(segment.start <= beforeIndex && beforeIndex <= segment.end); 184 ASSERT(is<RenderText>(segment.renderer)); 185 if (!m_style.shouldHyphenate || run.type() != TextFragment::NonWhitespace) 186 return std::nullopt; 187 188 auto runStart = segment.toSegmentPosition(run.start()); 189 auto before = segment.toSegmentPosition(beforeIndex) - runStart; 190 auto substringForHyphenation = StringView(segment.text).substring(runStart, run.end() - run.start()); 191 auto hyphenLocation = lastHyphenLocation(substringForHyphenation, before, m_style.locale); 192 if (hyphenLocation) 193 return segment.toRenderPosition(hyphenLocation + runStart); 194 return std::nullopt; 175 195 } 176 196 … … 220 240 if (startPosition == endPosition) 221 241 return 0; 222 unsigned segmentFrom = s tartPosition - segment.start;223 unsigned segmentTo = endPosition - segment.start;242 unsigned segmentFrom = segment.toSegmentPosition(startPosition); 243 unsigned segmentTo = segment.toSegmentPosition(endPosition); 224 244 bool measureWithEndSpace = m_style.collapseWhitespace && segmentTo < segment.text.length() && segment.text[segmentTo] == ' '; 225 245 if (measureWithEndSpace) -
trunk/Source/WebCore/rendering/SimpleLineLayoutTextFragmentIterator.h
r210456 r211108 44 44 enum Type { ContentEnd, SoftLineBreak, HardLineBreak, Whitespace, NonWhitespace }; 45 45 TextFragment() = default; 46 TextFragment(unsigned start, unsigned end, float width, Type type, bool isLastInRenderer = false, bool overlapsToNextRenderer = false, bool isCollapsed = false, bool isCollapsible = false )46 TextFragment(unsigned start, unsigned end, float width, Type type, bool isLastInRenderer = false, bool overlapsToNextRenderer = false, bool isCollapsed = false, bool isCollapsible = false, bool hasHyphen = false) 47 47 : m_start(start) 48 48 , m_end(end) … … 53 53 , m_isCollapsed(isCollapsed) 54 54 , m_isCollapsible(isCollapsible) 55 , m_hasHyphen(hasHyphen) 55 56 { 56 57 } … … 65 66 bool isCollapsed() const { return m_isCollapsed; } 66 67 bool isCollapsible() const { return m_isCollapsible; } 68 bool hasHyphen() const { return m_hasHyphen; } 67 69 68 70 bool isEmpty() const { return start() == end() && !isLineBreak(); } 69 71 TextFragment split(unsigned splitPosition, const TextFragmentIterator&); 72 TextFragment splitWithHyphen(unsigned hyphenPosition, const TextFragmentIterator&); 70 73 bool operator==(const TextFragment& other) const 71 74 { … … 77 80 && m_overlapsToNextRenderer == other.m_overlapsToNextRenderer 78 81 && m_isCollapsed == other.m_isCollapsed 79 && m_isCollapsible == other.m_isCollapsible; 82 && m_isCollapsible == other.m_isCollapsible 83 && m_hasHyphen == other.m_hasHyphen; 80 84 } 81 85 … … 89 93 bool m_isCollapsed { false }; 90 94 bool m_isCollapsible { false }; 95 bool m_hasHyphen { false }; 91 96 }; 92 97 TextFragment nextTextFragment(float xPosition = 0); 93 98 void revertToEndOfFragment(const TextFragment&); 99 100 // FIXME: These functions below should be decoupled from the text iterator. 94 101 float textWidth(unsigned startPosition, unsigned endPosition, float xPosition) const; 102 std::optional<unsigned> lastHyphenPosition(const TextFragmentIterator::TextFragment& run, unsigned beforeIndex) const; 95 103 96 104 struct Style { … … 109 117 float wordSpacing; 110 118 unsigned tabWidth; 119 bool shouldHyphenate; 120 float hyphenStringWidth; 111 121 AtomicString locale; 112 122 }; … … 143 153 }; 144 154 145 TextFragment newFragment(*this);155 TextFragment rightSide(*this); 146 156 m_end = splitPosition; 147 157 updateFragmentProperties(*this); 148 158 149 newFragment.m_start = splitPosition; 150 updateFragmentProperties(newFragment); 151 return newFragment; 159 rightSide.m_start = splitPosition; 160 updateFragmentProperties(rightSide); 161 return rightSide; 162 } 163 164 inline TextFragmentIterator::TextFragment TextFragmentIterator::TextFragment::splitWithHyphen(unsigned hyphenPosition, 165 const TextFragmentIterator& textFragmentIterator) 166 { 167 ASSERT(textFragmentIterator.style().shouldHyphenate); 168 auto rightSide = split(hyphenPosition, textFragmentIterator); 169 m_hasHyphen = true; 170 m_width += textFragmentIterator.style().hyphenStringWidth; 171 return rightSide; 152 172 } 153 173 -
trunk/Source/WebCore/rendering/line/BreakingContext.h
r208985 r211108 688 688 689 689 float maxPrefixWidth = availableWidth - xPos - hyphenWidth - lastSpaceWordSpacing; 690 // If the maximum width available for the prefix before the hyphen is small, then it is very unlikely 691 // that an hyphenation opportunity exists, so do not bother to look for it. 692 if (maxPrefixWidth <= font.pixelSize() * 5 / 4) 690 if (!enoughWidthForHyphenation(maxPrefixWidth, font.pixelSize())) 693 691 return; 694 692
Note: See TracChangeset
for help on using the changeset viewer.