void RenderMultiColumnSet::prepareForLayout() { RenderBlockFlow* multicolBlock = toRenderBlockFlow(parent()); const RenderStyle& multicolStyle = multicolBlock->style(); // Set box logical top. ASSERT(!previousSiblingBox() || !previousSiblingBox()->isRenderMultiColumnSet()); // FIXME: multiple set not implemented; need to examine previous set to calculate the correct logical top. setLogicalTop(multicolBlock->borderAndPaddingBefore()); // Set box width. updateLogicalWidth(); if (multicolBlock->multiColumnFlowThread()->requiresBalancing()) { // Set maximum column height. We will not stretch beyond this. m_maxColumnHeight = RenderFlowThread::maxLogicalHeight(); if (!multicolStyle.logicalHeight().isAuto()) { m_maxColumnHeight = multicolBlock->computeContentLogicalHeight(multicolStyle.logicalHeight()); if (m_maxColumnHeight == -1) m_maxColumnHeight = RenderFlowThread::maxLogicalHeight(); } if (!multicolStyle.logicalMaxHeight().isUndefined()) { LayoutUnit logicalMaxHeight = multicolBlock->computeContentLogicalHeight(multicolStyle.logicalMaxHeight()); if (logicalMaxHeight != -1 && m_maxColumnHeight > logicalMaxHeight) m_maxColumnHeight = logicalMaxHeight; } m_maxColumnHeight = heightAdjustedForSetOffset(m_maxColumnHeight); m_computedColumnHeight = 0; // Restart balancing. } else setAndConstrainColumnHeight(heightAdjustedForSetOffset(multicolBlock->multiColumnFlowThread()->columnHeightAvailable())); clearForcedBreaks(); // Nuke previously stored minimum column height. Contents may have changed for all we know. m_minimumColumnHeight = 0; }
LayoutUnit RenderMultiColumnSet::heightAdjustedForSetOffset(LayoutUnit height) const { RenderBlockFlow* multicolBlock = toRenderBlockFlow(parent()); LayoutUnit contentLogicalTop = logicalTop() - multicolBlock->borderAndPaddingBefore(); height -= contentLogicalTop; return std::max(height, LayoutUnit::fromPixel(1)); // Let's avoid zero height, as that would probably cause an infinite amount of columns to be created. }
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()) { }
static void createTextRuns(Layout::RunVector& runs, RenderBlockFlow& flow, unsigned& lineCount) { LayoutUnit borderAndPaddingBefore = flow.borderAndPaddingBefore(); LayoutUnit lineHeight = lineHeightFromFlow(flow); LineState line; bool isEndOfContent = false; TextFragmentIterator textFragmentIterator = TextFragmentIterator(flow); do { flow.setLogicalHeight(lineHeight * lineCount + borderAndPaddingBefore); LineState previousLine = line; unsigned previousRunCount = runs.size(); line = LineState(); updateLineConstrains(flow, line, !lineCount); isEndOfContent = createLineRuns(line, previousLine, runs, textFragmentIterator); closeLineEndingAndAdjustRuns(line, runs, previousRunCount, lineCount, textFragmentIterator); } while (!isEndOfContent); }
LayoutUnit RenderMultiColumnSet::heightAdjustedForSetOffset(LayoutUnit height) const { // Adjust for the top offset within the content box of the multicol container (containing // block), unless this is the first set. We know that the top offset for the first set will be // zero, but if the multicol container has non-zero top border or padding, the set's top offset // (initially being 0 and relative to the border box) will be negative until it has been laid // out. Had we used this bogus offset, we would calculate the wrong height, and risk performing // a wasted layout iteration. Of course all other sets (if any) have this problem in the first // layout pass too, but there's really nothing we can do there until the flow thread has been // laid out anyway. if (previousSiblingMultiColumnSet()) { RenderBlockFlow* multicolBlock = multiColumnBlockFlow(); LayoutUnit contentLogicalTop = logicalTop() - multicolBlock->borderAndPaddingBefore(); height -= contentLogicalTop; } return max(height, LayoutUnit(1)); // Let's avoid zero height, as that would probably cause an infinite amount of columns to be created. }
void createTextRuns(Layout::RunVector& runs, unsigned& lineCount, RenderBlockFlow& flow, RenderText& textRenderer) { const Style style(flow.style()); const CharacterType* text = textRenderer.text()->characters<CharacterType>(); const unsigned textLength = textRenderer.textLength(); LayoutUnit borderAndPaddingBefore = flow.borderAndPaddingBefore(); LayoutUnit lineHeight = lineHeightFromFlow(flow); LazyLineBreakIterator lineBreakIterator(textRenderer.text(), flow.style().locale()); unsigned lineEnd = 0; while (lineEnd < textLength) { if (style.collapseWhitespace) lineEnd = skipWhitespaces(text, lineEnd, textLength, style.preserveNewline); unsigned lineStart = lineEnd; // LineWidth reads the current y position from the flow so keep it updated. flow.setLogicalHeight(lineHeight * lineCount + borderAndPaddingBefore); LineWidth lineWidth(flow, false, DoNotIndentText); auto lineRuns = createLineRuns(lineStart, lineWidth, lineBreakIterator, style, text, textLength, textRenderer); lineEnd = lineRuns.last().end; if (lineStart == lineEnd) continue; lineRuns.last().isEndOfLine = true; float lineLeft = computeLineLeft(style.textAlign, lineWidth); adjustRunOffsets(lineRuns, lineLeft); for (unsigned i = 0; i < lineRuns.size(); ++i) runs.append(lineRuns[i]); ++lineCount; } }
LayoutUnit RootInlineBox::lineSnapAdjustment(LayoutUnit delta) const { // If our block doesn't have snapping turned on, do nothing. // FIXME: Implement bounds snapping. if (blockFlow().style().lineSnap() == LineSnapNone) return 0; // Get the current line grid and offset. LayoutState* layoutState = blockFlow().view().layoutState(); RenderBlockFlow* lineGrid = layoutState->lineGrid(); LayoutSize lineGridOffset = layoutState->lineGridOffset(); if (!lineGrid || lineGrid->style().writingMode() != blockFlow().style().writingMode()) return 0; // Get the hypothetical line box used to establish the grid. RootInlineBox* lineGridBox = lineGrid->lineGridBox(); if (!lineGridBox) return 0; LayoutUnit lineGridBlockOffset = lineGrid->isHorizontalWritingMode() ? lineGridOffset.height() : lineGridOffset.width(); LayoutUnit blockOffset = blockFlow().isHorizontalWritingMode() ? layoutState->layoutOffset().height() : layoutState->layoutOffset().width(); // Now determine our position on the grid. Our baseline needs to be adjusted to the nearest baseline multiple // as established by the line box. // FIXME: Need to handle crazy line-box-contain values that cause the root line box to not be considered. I assume // the grid should honor line-box-contain. LayoutUnit gridLineHeight = lineGridBox->lineBottomWithLeading() - lineGridBox->lineTopWithLeading(); if (!gridLineHeight) return 0; LayoutUnit lineGridFontAscent = lineGrid->style().fontMetrics().ascent(baselineType()); LayoutUnit lineGridFontHeight = lineGridBox->logicalHeight(); LayoutUnit firstTextTop = lineGridBlockOffset + lineGridBox->logicalTop(); LayoutUnit firstLineTopWithLeading = lineGridBlockOffset + lineGridBox->lineTopWithLeading(); LayoutUnit firstBaselinePosition = firstTextTop + lineGridFontAscent; LayoutUnit currentTextTop = blockOffset + logicalTop() + delta; LayoutUnit currentFontAscent = blockFlow().style().fontMetrics().ascent(baselineType()); LayoutUnit currentBaselinePosition = currentTextTop + currentFontAscent; LayoutUnit lineGridPaginationOrigin = isHorizontal() ? layoutState->lineGridPaginationOrigin().height() : layoutState->lineGridPaginationOrigin().width(); // If we're paginated, see if we're on a page after the first one. If so, the grid resets on subsequent pages. // FIXME: If the grid is an ancestor of the pagination establisher, then this is incorrect. LayoutUnit pageLogicalTop = 0; if (layoutState->isPaginated() && layoutState->pageLogicalHeight()) { pageLogicalTop = blockFlow().pageLogicalTopForOffset(lineTopWithLeading() + delta); if (pageLogicalTop > firstLineTopWithLeading) firstTextTop = pageLogicalTop + lineGridBox->logicalTop() - lineGrid->borderAndPaddingBefore() + lineGridPaginationOrigin; } if (blockFlow().style().lineSnap() == LineSnapContain) { // Compute the desired offset from the text-top of a grid line. // Look at our height (logicalHeight()). // Look at the total available height. It's going to be (textBottom - textTop) + (n-1)*(multiple with leading) // where n is number of grid lines required to enclose us. if (logicalHeight() <= lineGridFontHeight) firstTextTop += (lineGridFontHeight - logicalHeight()) / 2; else { LayoutUnit numberOfLinesWithLeading = ceilf(static_cast<float>(logicalHeight() - lineGridFontHeight) / gridLineHeight); LayoutUnit totalHeight = lineGridFontHeight + numberOfLinesWithLeading * gridLineHeight; firstTextTop += (totalHeight - logicalHeight()) / 2; } firstBaselinePosition = firstTextTop + currentFontAscent; } else firstBaselinePosition = firstTextTop + lineGridFontAscent; // If we're above the first line, just push to the first line. if (currentBaselinePosition < firstBaselinePosition) return delta + firstBaselinePosition - currentBaselinePosition; // Otherwise we're in the middle of the grid somewhere. Just push to the next line. LayoutUnit baselineOffset = currentBaselinePosition - firstBaselinePosition; LayoutUnit remainder = roundToInt(baselineOffset) % roundToInt(gridLineHeight); LayoutUnit result = delta; if (remainder) result += gridLineHeight - remainder; // If we aren't paginated we can return the result. if (!layoutState->isPaginated() || !layoutState->pageLogicalHeight() || result == delta) return result; // We may end up shifted to a new page. We need to do a re-snap when that happens. LayoutUnit newPageLogicalTop = blockFlow().pageLogicalTopForOffset(lineBottomWithLeading() + result); if (newPageLogicalTop == pageLogicalTop) return result; // Put ourselves at the top of the next page to force a snap onto the new grid established by that page. return lineSnapAdjustment(newPageLogicalTop - (blockOffset + lineTopWithLeading())); }