/* * 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(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(y, 0); return std::min(y / m_lineHeight, m_layout.lineCount() - 1); } Range RunResolver::rangeForRect(const LayoutRect& rect) const { if (!m_lineHeight) return Range(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(end(), end()); auto rangeEnd = rangeBegin; ASSERT(lastLine >= firstLine); rangeEnd.advanceLines(lastLine - firstLine + 1); return Range(rangeBegin, rangeEnd); } Range RunResolver::rangeForRenderer(const RenderObject& renderer) const { if (begin() == end()) return Range(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(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(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) { } } }