| /* |
| * Copyright (C) 2014 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "SimpleLineLayoutResolver.h" |
| |
| #include "InlineTextBoxStyle.h" |
| #include "RenderBlockFlow.h" |
| #include "RenderObject.h" |
| #include "SimpleLineLayoutFunctions.h" |
| |
| namespace WebCore { |
| namespace SimpleLineLayout { |
| |
| static FloatPoint linePosition(float logicalLeft, float logicalTop) |
| { |
| return FloatPoint(logicalLeft, roundf(logicalTop)); |
| } |
| |
| static FloatSize lineSize(float logicalLeft, float logicalRight, float height) |
| { |
| return FloatSize(logicalRight - logicalLeft, height); |
| } |
| |
| RunResolver::Run::Run(const Iterator& iterator) |
| : m_iterator(iterator) |
| { |
| } |
| |
| FloatRect RunResolver::Run::rect() const |
| { |
| auto& run = m_iterator.simpleRun(); |
| auto& resolver = m_iterator.resolver(); |
| float baseline = computeBaselinePosition(); |
| FloatPoint position = linePosition(run.logicalLeft, baseline - resolver.m_ascent); |
| FloatSize size = lineSize(run.logicalLeft, run.logicalRight, resolver.m_ascent + resolver.m_descent + resolver.m_visualOverflowOffset); |
| bool moveLineBreakToBaseline = false; |
| if (run.start == run.end && m_iterator != resolver.begin() && m_iterator.inQuirksMode()) { |
| auto previousRun = m_iterator; |
| --previousRun; |
| moveLineBreakToBaseline = !previousRun.simpleRun().isEndOfLine; |
| } |
| if (moveLineBreakToBaseline) |
| return FloatRect(FloatPoint(position.x(), baseline), FloatSize(size.width(), std::max<float>(0, resolver.m_ascent - resolver.m_baseline.toFloat()))); |
| return FloatRect(position, size); |
| } |
| |
| StringView RunResolver::Run::text() const |
| { |
| auto& run = m_iterator.simpleRun(); |
| ASSERT(run.start < run.end); |
| auto& segment = m_iterator.resolver().m_flowContents.segmentForRun(run.start, run.end); |
| // We currently split runs on segment boundaries (different RenderObject). |
| ASSERT(run.end <= segment.end); |
| return StringView(segment.text).substring(run.start - segment.start, run.end - run.start); |
| } |
| |
| RunResolver::Iterator::Iterator(const RunResolver& resolver, unsigned runIndex, unsigned lineIndex) |
| : m_resolver(resolver) |
| , m_runIndex(runIndex) |
| , m_lineIndex(lineIndex) |
| { |
| } |
| |
| RunResolver::Iterator& RunResolver::Iterator::advance() |
| { |
| if (simpleRun().isEndOfLine) |
| ++m_lineIndex; |
| ++m_runIndex; |
| return *this; |
| } |
| |
| RunResolver::Iterator& RunResolver::Iterator::advanceLines(unsigned lineCount) |
| { |
| unsigned runCount = m_resolver.m_layout.runCount(); |
| if (runCount == m_resolver.m_layout.lineCount()) { |
| m_runIndex = std::min(runCount, m_runIndex + lineCount); |
| m_lineIndex = m_runIndex; |
| return *this; |
| } |
| unsigned target = m_lineIndex + lineCount; |
| while (m_lineIndex < target && m_runIndex < runCount) |
| advance(); |
| |
| return *this; |
| } |
| |
| RunResolver::RunResolver(const RenderBlockFlow& flow, const Layout& layout) |
| : m_flowRenderer(flow) |
| , m_layout(layout) |
| , m_flowContents(flow) |
| , m_lineHeight(lineHeightFromFlow(flow)) |
| , m_baseline(baselineFromFlow(flow)) |
| , m_borderAndPaddingBefore(flow.borderAndPaddingBefore()) |
| , m_ascent(flow.style().fontCascade().fontMetrics().ascent()) |
| , m_descent(flow.style().fontCascade().fontMetrics().descent()) |
| , m_visualOverflowOffset(visualOverflowForDecorations(flow.style(), nullptr).bottom) |
| , m_inQuirksMode(flow.document().inQuirksMode()) |
| { |
| } |
| |
| unsigned RunResolver::lineIndexForHeight(LayoutUnit height, IndexType type) const |
| { |
| ASSERT(m_lineHeight); |
| float y = height - m_borderAndPaddingBefore; |
| // Lines may overlap, adjust to get the first or last line at this height. |
| if (type == IndexType::First) |
| y += m_lineHeight - (m_baseline + m_descent); |
| else |
| y -= m_baseline - m_ascent; |
| y = std::max<float>(y, 0); |
| return std::min<unsigned>(y / m_lineHeight, m_layout.lineCount() - 1); |
| } |
| |
| Range<RunResolver::Iterator> RunResolver::rangeForRect(const LayoutRect& rect) const |
| { |
| if (!m_lineHeight) |
| return Range<Iterator>(begin(), end()); |
| |
| unsigned firstLine = lineIndexForHeight(rect.y(), IndexType::First); |
| unsigned lastLine = std::max(firstLine, lineIndexForHeight(rect.maxY(), IndexType::Last)); |
| |
| auto rangeBegin = begin().advanceLines(firstLine); |
| if (rangeBegin == end()) |
| return Range<Iterator>(end(), end()); |
| auto rangeEnd = rangeBegin; |
| ASSERT(lastLine >= firstLine); |
| rangeEnd.advanceLines(lastLine - firstLine + 1); |
| return Range<Iterator>(rangeBegin, rangeEnd); |
| } |
| |
| Range<RunResolver::Iterator> RunResolver::rangeForRenderer(const RenderObject& renderer) const |
| { |
| if (begin() == end()) |
| return Range<Iterator>(end(), end()); |
| FlowContents::Iterator segment = m_flowContents.begin(); |
| auto run = begin(); |
| ASSERT(segment->start <= (*run).start()); |
| // Move run to the beginning of the segment. |
| while (&segment->renderer != &renderer && run != end()) { |
| if ((*run).start() == segment->start && (*run).end() == segment->end) { |
| ++run; |
| ++segment; |
| } else if ((*run).start() < segment->end) |
| ++run; |
| else |
| ++segment; |
| ASSERT(segment != m_flowContents.end()); |
| } |
| // Do we actually have a run for this renderer? |
| // Collapsed whitespace with dedicated renderer could end up with no run at all. |
| if (run == end() || (segment->start != segment->end && segment->end <= (*run).start())) |
| return Range<Iterator>(end(), end()); |
| |
| auto rangeBegin = run; |
| // Move beyond the end of the segment. |
| while (run != end() && (*run).start() < segment->end) |
| ++run; |
| // Special case when segment == run. |
| if (run == rangeBegin) |
| ++run; |
| return Range<Iterator>(rangeBegin, run); |
| } |
| |
| LineResolver::Iterator::Iterator(RunResolver::Iterator runIterator) |
| : m_runIterator(runIterator) |
| { |
| } |
| |
| const FloatRect LineResolver::Iterator::operator*() const |
| { |
| unsigned currentLine = m_runIterator.lineIndex(); |
| auto it = m_runIterator; |
| FloatRect rect = (*it).rect(); |
| while (it.advance().lineIndex() == currentLine) |
| rect.unite((*it).rect()); |
| return rect; |
| } |
| |
| LineResolver::LineResolver(const RenderBlockFlow& flow, const Layout& layout) |
| : m_runResolver(flow, layout) |
| { |
| } |
| |
| } |
| } |