Modified: trunk/Source/WebCore/rendering/RenderBlockFlow.h (212467 => 212468)
--- trunk/Source/WebCore/rendering/RenderBlockFlow.h 2017-02-16 23:01:45 UTC (rev 212467)
+++ trunk/Source/WebCore/rendering/RenderBlockFlow.h 2017-02-16 23:02:56 UTC (rev 212468)
@@ -395,16 +395,16 @@
bool needsLayoutAfterRegionRangeChange() const override;
WEBCORE_EXPORT RenderText* findClosestTextAtAbsolutePoint(const FloatPoint&);
-protected:
- void computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const override;
-
// A page break is required at some offset due to space shortage in the current fragmentainer.
void setPageBreak(LayoutUnit offset, LayoutUnit spaceShortage);
-
// Update minimum page height required to avoid fragmentation where it shouldn't occur (inside
// unbreakable content, between orphans and widows, etc.). This will be used as a hint to the
// column balancer to help set a good minimum column height.
void updateMinimumPageHeight(LayoutUnit offset, LayoutUnit minHeight);
+
+protected:
+ void computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const override;
+
bool pushToNextPageWithMinimumLogicalHeight(LayoutUnit& adjustment, LayoutUnit logicalOffset, LayoutUnit minimumLogicalHeight) const;
// If the child is unsplittable and can't fit on the current page, return the top of the next page/column.
Modified: trunk/Source/WebCore/rendering/SimpleLineLayout.cpp (212467 => 212468)
--- trunk/Source/WebCore/rendering/SimpleLineLayout.cpp 2017-02-16 23:01:45 UTC (rev 212467)
+++ trunk/Source/WebCore/rendering/SimpleLineLayout.cpp 2017-02-16 23:02:56 UTC (rev 212468)
@@ -907,6 +907,118 @@
++lineCount;
}
+struct PaginatedLine {
+ LayoutUnit top;
+ LayoutUnit bottom;
+ LayoutUnit height; // Same value for each lines atm.
+};
+using PaginatedLines = Vector<PaginatedLine, 20>;
+
+static PaginatedLine computeLineTopAndBottomWithOverflow(const RenderBlockFlow& flow, unsigned lineIndex, Layout::SimplePaginationStruts& struts)
+{
+ // FIXME: Add visualOverflowForDecorations.
+ auto& fontMetrics = flow.style().fontCascade().fontMetrics();
+ auto ascent = fontMetrics.floatAscent();
+ auto descent = fontMetrics.floatDescent();
+ auto lineHeight = lineHeightFromFlow(flow);
+ LayoutUnit offset = flow.borderAndPaddingBefore();
+ for (auto& strut : struts) {
+ if (strut.lineBreak > lineIndex)
+ break;
+ offset += strut.offset;
+ }
+ if (ascent + descent <= lineHeight) {
+ auto topPosition = lineIndex * lineHeight + offset;
+ return { topPosition, topPosition + lineHeight, lineHeight };
+ }
+ auto baseline = baselineFromFlow(flow);
+ auto topPosition = lineIndex * lineHeight + offset + baseline - ascent;
+ auto bottomPosition = topPosition + ascent + descent;
+ return { topPosition, bottomPosition, bottomPosition - topPosition };
+}
+
+static unsigned computeLineBreakIndex(unsigned breakCandidate, unsigned lineCount, unsigned widows, const Layout::SimplePaginationStruts& struts)
+{
+ // First line does not fit the current page.
+ if (!breakCandidate)
+ return breakCandidate;
+
+ auto remainingLineCount = lineCount - breakCandidate;
+ if (widows <= remainingLineCount)
+ return breakCandidate;
+
+ // Only break after the first line with widows.
+ auto lineBreak = std::max<int>(lineCount - widows, 1);
+ // Break on current page only.
+ if (struts.isEmpty())
+ return lineBreak;
+ ASSERT(struts.last().lineBreak + 1 < lineCount);
+ return std::max<unsigned>(struts.last().lineBreak + 1, lineBreak);
+}
+
+static void setPageBreakForLine(unsigned lineBreak, PaginatedLines& lines, RenderBlockFlow& flow)
+{
+ if (!lineBreak) {
+ // When the line does not fit the current page, just add a page break in front.
+ auto line = lines.first();
+ flow.setPageBreak(line.top, flow.pageRemainingLogicalHeightForOffset(line.top, RenderBlockFlow::ExcludePageBoundary));
+ return;
+ }
+ auto beforeLineBreak = lines.at(lineBreak - 1);
+ auto spaceShortage = flow.pageRemainingLogicalHeightForOffset(beforeLineBreak.top, RenderBlockFlow::ExcludePageBoundary) - beforeLineBreak.height;
+ flow.setPageBreak(beforeLineBreak.bottom, spaceShortage);
+}
+
+static LayoutUnit computeOffsetAfterLineBreak(LayoutUnit lineBreakPosition, bool isFirstLine, bool atTheTopOfColumnOrPage, const RenderBlockFlow& flow)
+{
+ // No offset for top of the page lines unless widows pushed the line break.
+ LayoutUnit offset = isFirstLine ? flow.borderAndPaddingBefore() : LayoutUnit();
+ if (atTheTopOfColumnOrPage)
+ return offset;
+ return offset + flow.pageRemainingLogicalHeightForOffset(lineBreakPosition, RenderBlockFlow::ExcludePageBoundary);
+}
+
+static void updateMinimumPageHeight(RenderBlockFlow& flow, unsigned lineCount)
+{
+ auto& style = flow.style();
+ auto widows = style.hasAutoWidows() ? 1 : std::max<int>(style.widows(), 1);
+ auto orphans = style.hasAutoOrphans() ? 1 : std::max<int>(style.orphans(), 1);
+ auto minimumLineCount = std::min<unsigned>(std::max(widows, orphans), lineCount);
+ flow.updateMinimumPageHeight(0, minimumLineCount * lineHeightFromFlow(flow));
+}
+
+static void adjustLinePositionsForPagination(Layout::RunVector& runs, Layout::SimplePaginationStruts& struts,
+ RenderBlockFlow& flow, unsigned lineCount)
+{
+ updateMinimumPageHeight(flow, lineCount);
+ // First pass with no pagination offset?
+ if (!flow.pageLogicalHeightForOffset(0))
+ return;
+ unsigned lineIndex = 0;
+ auto widows = flow.style().hasAutoWidows() ? 1 : std::max<int>(flow.style().widows(), 1);
+ PaginatedLines lines;
+ for (auto& run : runs) {
+ if (!run.isEndOfLine)
+ continue;
+
+ auto line = computeLineTopAndBottomWithOverflow(flow, lineIndex, struts);
+ lines.append(line);
+ auto remainingHeight = flow.pageRemainingLogicalHeightForOffset(line.top, RenderBlockFlow::ExcludePageBoundary);
+ auto atTheTopOfColumnOrPage = flow.pageLogicalHeightForOffset(line.top) == remainingHeight;
+ if (line.height > remainingHeight || (atTheTopOfColumnOrPage && lineIndex)) {
+ auto lineBreakIndex = computeLineBreakIndex(lineIndex, lineCount, widows, struts);
+ // Are we still at the top of the column/page?
+ atTheTopOfColumnOrPage = atTheTopOfColumnOrPage ? lineIndex == lineBreakIndex : false;
+ setPageBreakForLine(lineBreakIndex, lines, flow);
+ struts.append({ lineBreakIndex, computeOffsetAfterLineBreak(lines[lineBreakIndex].top, !lineBreakIndex, atTheTopOfColumnOrPage, flow) });
+ // Recompute line positions that we already visited but window break pushed them to a new page.
+ for (auto i = lineBreakIndex; i < lines.size(); ++i)
+ lines.at(i) = computeLineTopAndBottomWithOverflow(flow, i, struts);
+ }
+ ++lineIndex;
+ }
+}
+
static void createTextRuns(Layout::RunVector& runs, RenderBlockFlow& flow, unsigned& lineCount)
{
LayoutUnit borderAndPaddingBefore = flow.borderAndPaddingBefore();
@@ -931,20 +1043,23 @@
{
unsigned lineCount = 0;
Layout::RunVector runs;
-
createTextRuns(runs, flow, lineCount);
- return Layout::create(runs, lineCount);
+ Layout::SimplePaginationStruts struts;
+ if (flow.view().layoutState() && flow.view().layoutState()->isPaginated())
+ adjustLinePositionsForPagination(runs, struts, flow, lineCount);
+ return Layout::create(runs, struts, lineCount);
}
-std::unique_ptr<Layout> Layout::create(const RunVector& runVector, unsigned lineCount)
+std::unique_ptr<Layout> Layout::create(const RunVector& runVector, SimplePaginationStruts& struts, unsigned lineCount)
{
void* slot = WTF::fastMalloc(sizeof(Layout) + sizeof(Run) * runVector.size());
- return std::unique_ptr<Layout>(new (NotNull, slot) Layout(runVector, lineCount));
+ return std::unique_ptr<Layout>(new (NotNull, slot) Layout(runVector, struts, lineCount));
}
-Layout::Layout(const RunVector& runVector, unsigned lineCount)
+Layout::Layout(const RunVector& runVector, SimplePaginationStruts& struts, unsigned lineCount)
: m_lineCount(lineCount)
, m_runCount(runVector.size())
+ , m_paginationStruts(WTFMove(struts))
{
memcpy(m_runs, runVector.data(), m_runCount * sizeof(Run));
}
Modified: trunk/Source/WebCore/rendering/SimpleLineLayout.h (212467 => 212468)
--- trunk/Source/WebCore/rendering/SimpleLineLayout.h 2017-02-16 23:01:45 UTC (rev 212467)
+++ trunk/Source/WebCore/rendering/SimpleLineLayout.h 2017-02-16 23:02:56 UTC (rev 212468)
@@ -66,11 +66,17 @@
ExpansionBehavior expansionBehavior { ForbidLeadingExpansion | ForbidTrailingExpansion };
};
+struct SimplePaginationStrut {
+ unsigned lineBreak;
+ float offset;
+};
+
class Layout {
WTF_MAKE_FAST_ALLOCATED;
public:
- typedef Vector<Run, 10> RunVector;
- static std::unique_ptr<Layout> create(const RunVector&, unsigned lineCount);
+ using RunVector = Vector<Run, 10>;
+ using SimplePaginationStruts = Vector<SimplePaginationStrut, 4>;
+ static std::unique_ptr<Layout> create(const RunVector&, SimplePaginationStruts&, unsigned lineCount);
unsigned lineCount() const { return m_lineCount; }
@@ -77,11 +83,14 @@
unsigned runCount() const { return m_runCount; }
const Run& runAt(unsigned i) const { return m_runs[i]; }
+ bool isPaginated() const { return !m_paginationStruts.isEmpty(); }
+ const SimplePaginationStruts& struts() const { return m_paginationStruts; }
private:
- Layout(const RunVector&, unsigned lineCount);
+ Layout(const RunVector&, SimplePaginationStruts&, unsigned lineCount);
unsigned m_lineCount;
unsigned m_runCount;
+ SimplePaginationStruts m_paginationStruts;
Run m_runs[0];
};