SimpleLineLayoutPagination.cpp [plain text]
#include "config.h"
#include "SimpleLineLayoutPagination.h"
#include "FontCascade.h"
#include "RenderBlockFlow.h"
#include "SimpleLineLayout.h"
#include "SimpleLineLayoutFunctions.h"
namespace WebCore {
namespace SimpleLineLayout {
struct PaginatedLine {
LayoutUnit top;
LayoutUnit bottom;
LayoutUnit height; };
using PaginatedLines = Vector<PaginatedLine, 20>;
static PaginatedLine computeLineTopAndBottomWithOverflow(const RenderBlockFlow& flow, unsigned lineIndex, Layout::SimpleLineStruts& struts)
{
auto& fontMetrics = flow.style().fontCascade().fontMetrics();
auto ascent = fontMetrics.ascent();
auto descent = fontMetrics.descent();
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, int orphansNeeded, int widowsNeeded,
const Layout::SimpleLineStruts& struts)
{
if (!breakCandidate)
return breakCandidate;
int widowsOnTheNextPage = lineCount - breakCandidate;
if (widowsNeeded <= widowsOnTheNextPage)
return breakCandidate;
auto lineBreak = std::max<int>(lineCount - widowsNeeded, 1);
if (orphansNeeded > lineBreak)
return breakCandidate;
if (struts.isEmpty())
return lineBreak;
ASSERT(struts.last().lineBreak + 1 < lineCount);
return std::max<unsigned>(struts.last().lineBreak + 1, lineBreak);
}
static LayoutUnit computeOffsetAfterLineBreak(LayoutUnit lineBreakPosition, bool isFirstLine, bool atTheTopOfColumnOrPage, const RenderBlockFlow& flow)
{
LayoutUnit offset = isFirstLine ? flow.borderAndPaddingBefore() : LayoutUnit();
if (atTheTopOfColumnOrPage)
return offset;
return offset + flow.pageRemainingLogicalHeightForOffset(lineBreakPosition, RenderBlockFlow::ExcludePageBoundary);
}
static void setPageBreakForLine(unsigned lineBreakIndex, PaginatedLines& lines, RenderBlockFlow& flow, Layout::SimpleLineStruts& struts,
bool atTheTopOfColumnOrPage)
{
auto line = lines.at(lineBreakIndex);
auto remainingLogicalHeight = flow.pageRemainingLogicalHeightForOffset(line.top, RenderBlockFlow::ExcludePageBoundary);
auto& style = flow.style();
auto firstLineDoesNotFit = !lineBreakIndex && line.height < flow.pageLogicalHeightForOffset(line.top);
auto orphanDoesNotFit = !style.hasAutoOrphans() && style.orphans() > (short)lineBreakIndex;
if (firstLineDoesNotFit || orphanDoesNotFit) {
auto firstLine = lines.first();
auto firstLineOverflowRect = computeOverflow(flow, LayoutRect(0, firstLine.top, 0, firstLine.height));
auto firstLineUpperOverhang = std::max<LayoutUnit>(-firstLineOverflowRect.y(), 0);
flow.setPaginationStrut(line.top + remainingLogicalHeight + firstLineUpperOverhang);
return;
}
if (atTheTopOfColumnOrPage)
flow.setPageBreak(line.top, line.height);
else
flow.setPageBreak(line.top, line.height - remainingLogicalHeight);
struts.append({ lineBreakIndex, computeOffsetAfterLineBreak(lines[lineBreakIndex].top, !lineBreakIndex, atTheTopOfColumnOrPage, flow) });
}
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));
}
void adjustLinePositionsForPagination(SimpleLineLayout::Layout& simpleLines, RenderBlockFlow& flow)
{
Layout::SimpleLineStruts struts;
auto lineCount = simpleLines.lineCount();
updateMinimumPageHeight(flow, lineCount);
if (!flow.pageLogicalHeightForOffset(0))
return;
unsigned lineIndex = 0;
auto widows = flow.style().hasAutoWidows() ? 1 : std::max<int>(flow.style().widows(), 1);
auto orphans = flow.style().hasAutoOrphans() ? 1 : std::max<int>(flow.style().orphans(), 1);
PaginatedLines lines;
for (unsigned runIndex = 0; runIndex < simpleLines.runCount(); ++runIndex) {
auto& run = simpleLines.runAt(runIndex);
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, orphans, widows, struts);
atTheTopOfColumnOrPage = atTheTopOfColumnOrPage ? lineIndex == lineBreakIndex : false;
setPageBreakForLine(lineBreakIndex, lines, flow, struts, atTheTopOfColumnOrPage);
for (auto i = lineBreakIndex; i < lines.size(); ++i)
lines.at(i) = computeLineTopAndBottomWithOverflow(flow, i, struts);
}
++lineIndex;
}
simpleLines.setLineStruts(WTFMove(struts));
}
}
}