Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/vaev-engine/dom/window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export struct Window {
}

[[clang::coro_wrapper]]
Generator<Print::Page> print(Print::Settings settings) const {
Generator<Print::Page> print(Print::Settings const& settings) const {
return Driver::print(_document.upgrade(), settings);
}
};
Expand Down
187 changes: 122 additions & 65 deletions src/vaev-engine/driver/print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ using namespace Karm;

namespace Vaev::Driver {

void _paintCornerMargin(Style::PageSpecifiedValues& pageStyle, Scene::Stack& stack, RectAu const& rect, Style::PageArea area) {
void _paintCornerMargin(Style::PageSpecifiedValues& pageStyle, Scene::Stack& stack, RectAu const& rect, Style::PageArea area, usize currentPage, Layout::RunningPositionMap& runningPosition) {
Layout::Tree tree{
.root = Layout::buildForPseudoElement(pageStyle.area(area)),
.root = Layout::buildForPseudoElement(pageStyle.area(area), currentPage, runningPosition),
.viewport = Layout::Viewport{.small = rect.size()}
};
auto [_, frag] = Layout::layoutCreateFragment(
Expand All @@ -37,10 +37,10 @@ void _paintCornerMargin(Style::PageSpecifiedValues& pageStyle, Scene::Stack& sta
Layout::paint(frag, stack);
}

void _paintMainMargin(Style::PageSpecifiedValues& pageStyle, Scene::Stack& stack, RectAu const& rect, Style::PageArea mainArea, Array<Style::PageArea, 3> subAreas) {
auto box = Layout::buildForPseudoElement(pageStyle.area(mainArea));
void _paintMainMargin(Style::PageSpecifiedValues& pageStyle, Scene::Stack& stack, RectAu const& rect, Style::PageArea mainArea, Array<Style::PageArea, 3> subAreas, usize currentPage, Layout::RunningPositionMap& runningPosition) {
auto box = Layout::buildForPseudoElement(pageStyle.area(mainArea), currentPage, runningPosition);
for (auto subArea : subAreas) {
box.add(Layout::buildForPseudoElement(pageStyle.area(subArea)));
box.add(Layout::buildForPseudoElement(pageStyle.area(subArea), currentPage, runningPosition));
}
Layout::Tree tree{
.root = std::move(box),
Expand All @@ -58,18 +58,18 @@ void _paintMainMargin(Style::PageSpecifiedValues& pageStyle, Scene::Stack& stack
Layout::paint(frag, stack);
}

void _paintMargins(Style::PageSpecifiedValues& pageStyle, RectAu pageRect, RectAu pageContent, Scene::Stack& stack) {
void _paintMargins(Style::PageSpecifiedValues& pageStyle, RectAu pageRect, RectAu pageContent, Scene::Stack& stack, usize currentPage, Layout::RunningPositionMap& runningPosition) {
// Compute all corner rects
auto topLeftMarginCornerRect = RectAu::fromTwoPoint(pageRect.topStart(), pageContent.topStart());
auto topRightMarginCornerRect = RectAu::fromTwoPoint(pageRect.topEnd(), pageContent.topEnd());
auto bottomLeftMarginCornerRect = RectAu::fromTwoPoint(pageRect.bottomStart(), pageContent.bottomStart());
auto bottomRightMarginCornerRect = RectAu::fromTwoPoint(pageRect.bottomEnd(), pageContent.bottomEnd());

// Paint corners
_paintCornerMargin(pageStyle, stack, topLeftMarginCornerRect, Style::PageArea::TOP_LEFT_CORNER);
_paintCornerMargin(pageStyle, stack, topRightMarginCornerRect, Style::PageArea::TOP_RIGHT_CORNER);
_paintCornerMargin(pageStyle, stack, bottomLeftMarginCornerRect, Style::PageArea::BOTTOM_LEFT_CORNER);
_paintCornerMargin(pageStyle, stack, bottomRightMarginCornerRect, Style::PageArea::BOTTOM_RIGHT_CORNER);
_paintCornerMargin(pageStyle, stack, topLeftMarginCornerRect, Style::PageArea::TOP_LEFT_CORNER, currentPage, runningPosition);
_paintCornerMargin(pageStyle, stack, topRightMarginCornerRect, Style::PageArea::TOP_RIGHT_CORNER, currentPage, runningPosition);
_paintCornerMargin(pageStyle, stack, bottomLeftMarginCornerRect, Style::PageArea::BOTTOM_LEFT_CORNER, currentPage, runningPosition);
_paintCornerMargin(pageStyle, stack, bottomRightMarginCornerRect, Style::PageArea::BOTTOM_RIGHT_CORNER, currentPage, runningPosition);

// Compute main area rects
auto topRect = RectAu::fromTwoPoint(topLeftMarginCornerRect.topEnd(), topRightMarginCornerRect.bottomStart());
Expand All @@ -78,98 +78,92 @@ void _paintMargins(Style::PageSpecifiedValues& pageStyle, RectAu pageRect, RectA
auto rightRect = RectAu::fromTwoPoint(topRightMarginCornerRect.bottomEnd(), bottomRightMarginCornerRect.topStart());

// Paint main areas
_paintMainMargin(pageStyle, stack, topRect, Style::PageArea::TOP, {Style::PageArea::TOP_LEFT, Style::PageArea::TOP_CENTER, Style::PageArea::TOP_RIGHT});
_paintMainMargin(pageStyle, stack, bottomRect, Style::PageArea::BOTTOM, {Style::PageArea::BOTTOM_LEFT, Style::PageArea::BOTTOM_CENTER, Style::PageArea::BOTTOM_RIGHT});
_paintMainMargin(pageStyle, stack, leftRect, Style::PageArea::LEFT, {Style::PageArea::LEFT_TOP, Style::PageArea::LEFT_MIDDLE, Style::PageArea::LEFT_BOTTOM});
_paintMainMargin(pageStyle, stack, rightRect, Style::PageArea::RIGHT, {Style::PageArea::RIGHT_TOP, Style::PageArea::RIGHT_MIDDLE, Style::PageArea::RIGHT_BOTTOM});
_paintMainMargin(pageStyle, stack, topRect, Style::PageArea::TOP, {Style::PageArea::TOP_LEFT, Style::PageArea::TOP_CENTER, Style::PageArea::TOP_RIGHT}, currentPage, runningPosition);
_paintMainMargin(pageStyle, stack, bottomRect, Style::PageArea::BOTTOM, {Style::PageArea::BOTTOM_LEFT, Style::PageArea::BOTTOM_CENTER, Style::PageArea::BOTTOM_RIGHT}, currentPage, runningPosition);
_paintMainMargin(pageStyle, stack, leftRect, Style::PageArea::LEFT, {Style::PageArea::LEFT_TOP, Style::PageArea::LEFT_MIDDLE, Style::PageArea::LEFT_BOTTOM}, currentPage, runningPosition);
_paintMainMargin(pageStyle, stack, rightRect, Style::PageArea::RIGHT, {Style::PageArea::RIGHT_TOP, Style::PageArea::RIGHT_MIDDLE, Style::PageArea::RIGHT_BOTTOM}, currentPage, runningPosition);
}

export Generator<Print::Page> print(Gc::Ref<Dom::Document> dom, Print::Settings settings) {
auto media = Style::Media::forPrint(settings);

Font::Database db;
if (not db.loadSystemFonts())
logWarn("not all fonts were properly loaded into database");

Style::Computer computer{
media, *dom->styleSheets, db
};
computer.build();
computer.styleDocument(*dom);

// MARK: Page and Margins --------------------------------------------------

Style::SpecifiedValues initialStyle = Style::SpecifiedValues::initial();
initialStyle.color = Gfx::BLACK;
initialStyle.setCustomProp("-vaev-url", {Css::Token::string(Io::format("\"{}\"", dom->url()))});
initialStyle.setCustomProp("-vaev-title", {Css::Token::string(Io::format("\"{}\"", dom->title()))});
initialStyle.setCustomProp("-vaev-datetime", {Css::Token::string(Io::format("\"{}\"", Sys::now()))});

// MARK: Page Content ------------------------------------------------------

Layout::Tree contentTree = {
Layout::build(dom),
};
auto canvasColor = fixupBackgrounds(computer, dom, contentTree);

struct PaginationContext {
Layout::Tree& contentTree;
Style::Media& media;
Print::Settings const& settings;
Style::Computer& computer;
Style::SpecifiedValues& initialStyle;
};

struct PageLayoutInfos {
RectAu pageRect;
RectAu pageContent;
Rc<Style::PageSpecifiedValues> pageStyle;
};

Pair<Vec<Layout::Breakpoint>, Vec<PageLayoutInfos>> collectBreakPointsAndRunningPositions(Layout::RunningPositionMap& runningPosition, PaginationContext& context) {
usize pageNumber = 0;
Layout::Breakpoint prevBreakpoint{
.endIdx = 0,
.advance = Layout::Breakpoint::Advance::WITHOUT_CHILDREN
};

usize pageNumber = 0;
Vec<Layout::Breakpoint> breakpoints = {prevBreakpoint};
Vec<PageLayoutInfos> pageInfos = {};

while (true) {
Style::Page page{
.name = ""s,
.number = pageNumber++,
.blank = false,
};

auto pageStyle = computer.computeFor(initialStyle, page);
Rc<Style::PageSpecifiedValues> pageStyle = context.computer.computeFor(context.initialStyle, page);
RectAu pageRect{
media.width / Au{media.resolution.toDppx()},
media.height / Au{media.resolution.toDppx()}
context.media.width / Au{context.media.resolution.toDppx()},
context.media.height / Au{context.media.resolution.toDppx()}
};

auto pageSize = pageRect.size().cast<f64>();

auto pageStack = makeRc<Scene::Stack>();

InsetsAu pageMargin;

if (settings.margins == Print::Margins::DEFAULT) {
if (context.settings.margins == Print::Margins::DEFAULT) {
Layout::Resolver resolver{};
pageMargin = {
resolver.resolve(pageStyle->style->margin->top, pageRect.height),
resolver.resolve(pageStyle->style->margin->end, pageRect.width),
resolver.resolve(pageStyle->style->margin->bottom, pageRect.height),
resolver.resolve(pageStyle->style->margin->start, pageRect.width),
};
} else if (settings.margins == Print::Margins::CUSTOM) {
pageMargin = settings.margins.custom.cast<Au>();
} else if (settings.margins == Print::Margins::MINIMUM) {
} else if (context.settings.margins == Print::Margins::CUSTOM) {
pageMargin = context.settings.margins.custom.template cast<Au>();
} else if (context.settings.margins == Print::Margins::MINIMUM) {
pageMargin = {};
}

RectAu pageContent = pageRect.shrink(pageMargin);

pageInfos.pushBack({pageRect, pageContent, pageStyle});

Layout::Viewport vp{
.small = pageContent.size(),
};

contentTree.viewport = vp;
contentTree.fc = {pageContent.size()};

if (settings.headerFooter and settings.margins != Print::Margins::NONE)
_paintMargins(*pageStyle, pageRect, pageContent, *pageStack);
context.contentTree.viewport = vp;
context.contentTree.fc = {pageContent.size()};

Layout::Input pageLayoutInput{
.knownSize = {pageContent.width, NONE},
.position = pageContent.topStart(),
.availableSpace = pageContent.size(),
.containingBlock = pageContent.size(),
.pageNumber = pageNumber,
};
pageLayoutInput.runningPosition = {&runningPosition};
context.contentTree.fc.enterDiscovery();

contentTree.fc.enterDiscovery();
auto outDiscovery = Layout::layout(
contentTree,
context.contentTree,
pageLayoutInput.withBreakpointTraverser(Layout::BreakpointTraverser(&prevBreakpoint))
);

Expand All @@ -178,13 +172,81 @@ export Generator<Print::Page> print(Gc::Ref<Dom::Document> dom, Print::Settings
? Layout::Breakpoint::classB(1, false)
: outDiscovery.breakpoint.unwrap();

contentTree.fc.leaveDiscovery();
context.contentTree.fc.leaveDiscovery();

breakpoints.pushBack(currBreakpoint);
if (outDiscovery.completelyLaidOut)
break;

prevBreakpoint = std::move(currBreakpoint);
}

return {breakpoints, pageInfos};
}

export Generator<Print::Page> print(Gc::Ref<Dom::Document> dom, Print::Settings const& settings) {
auto media = Style::Media::forPrint(settings);

Font::Database db;
if (not db.loadSystemFonts())
logWarn("not all fonts were properly loaded into database");

Style::Computer computer{
media, *dom->styleSheets, db
};
computer.build();
computer.styleDocument(*dom);

// MARK: Page and Margins --------------------------------------------------

Style::SpecifiedValues initialStyle = Style::SpecifiedValues::initial();
initialStyle.color = Gfx::BLACK;
initialStyle.setCustomProp("-vaev-url", {Css::Token::string(Io::format("\"{}\"", dom->url()))});
initialStyle.setCustomProp("-vaev-title", {Css::Token::string(Io::format("\"{}\"", dom->title()))});
initialStyle.setCustomProp("-vaev-datetime", {Css::Token::string(Io::format("\"{}\"", Sys::now()))});

// MARK: Page Content ------------------------------------------------------

Layout::Tree contentTree = {
Layout::build(dom),
};
auto canvasColor = fixupBackgrounds(computer, dom, contentTree);

Layout::RunningPositionMap runningPosition = {}; // Mapping the different Running positions to their respective names and their page.
PaginationContext paginationContext{
.contentTree = contentTree,
.media = media,
.settings = settings,
.computer = computer,
.initialStyle = initialStyle,
};
auto [breakpoints, pageInfos] = collectBreakPointsAndRunningPositions(runningPosition, paginationContext);

for (usize pageNumber = 0; pageNumber < breakpoints.len() - 1; pageNumber++) {
Style::Page page{
.name = ""s,
.number = pageNumber,
.blank = false,
};
auto pageStack = makeRc<Scene::Stack>();

auto infos = pageInfos[pageNumber];
Layout::Input pageLayoutInput{
.knownSize = {infos.pageContent.width, NONE},
.position = infos.pageContent.topStart(),
.availableSpace = infos.pageContent.size(),
.containingBlock = infos.pageContent.size(),
.pageNumber = pageNumber,
};
auto [outFragmentation, fragment] = Layout::layoutCreateFragment(
contentTree,
pageLayoutInput
.withBreakpointTraverser(Layout::BreakpointTraverser(&prevBreakpoint, &currBreakpoint))
.withBreakpointTraverser(Layout::BreakpointTraverser(&breakpoints[pageNumber], &breakpoints[pageNumber + 1]))
);

if (settings.headerFooter and settings.margins != Print::Margins::NONE)
_paintMargins(*pageInfos[pageNumber].pageStyle, pageInfos[pageNumber].pageRect, pageInfos[pageNumber].pageContent, *pageStack, page.number, runningPosition);

Layout::paint(fragment, *pageStack);
pageStack->prepare();

Expand All @@ -198,12 +260,7 @@ export Generator<Print::Page> print(Gc::Ref<Dom::Document> dom, Print::Settings
canvasColor
)
);

if (outFragmentation.completelyLaidOut)
break;

prevBreakpoint = std::move(currBreakpoint);
}
}

} // namespace Vaev::Driver
} // namespace Vaev::Driver
1 change: 1 addition & 0 deletions src/vaev-engine/layout/base/box.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ struct Group {

struct SVGRoot : SVG::Group {
Opt<ViewBox> viewBox;

SVGRoot(Rc<Style::SpecifiedValues> style)
: SVG::Group(style), viewBox(style->svg->viewBox) {}

Expand Down
3 changes: 3 additions & 0 deletions src/vaev-engine/layout/base/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export module Vaev.Engine:layout.input;

import :layout.breaks;
import :layout.fragment;
import :layout.runningPosition;

namespace Vaev::Layout {

Expand All @@ -25,6 +26,8 @@ export struct Input {
Vec2Au position = {};
Vec2Au availableSpace = {};
Vec2Au containingBlock = {};
MutCursor<RunningPositionMap> runningPosition = nullptr;
usize pageNumber = 0;

BreakpointTraverser breakpointTraverser = {};

Expand Down
15 changes: 13 additions & 2 deletions src/vaev-engine/layout/base/layout-impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,16 @@ Output layout(Tree& tree, Box& box, Input input) {
? input.breakpointTraverser.getEnd()
: NONE;

if (box.style->position.is<RunningPosition>()) {
return Output{
.size = {},
.completelyLaidOut = true,
.firstBaselineSet = {},
.lastBaselineSet = {},
};
}
auto parentFrag = input.fragment;

Frag currFrag(&box);
input.fragment = input.fragment ? &currFrag : nullptr;

Expand Down Expand Up @@ -336,8 +345,10 @@ Output layout(Tree& tree, Box& box, Input input) {

Output layout(Tree& tree, Input input) {
auto out = layout(tree, tree.root, input);
if (input.fragment)
layoutPositioned(tree, input.fragment->children()[0], input.containingBlock);
if (input.fragment) {
layoutPositioned(tree, input.fragment->children()[0], input.containingBlock, input);
}

return out;
}

Expand Down
1 change: 1 addition & 0 deletions src/vaev-engine/layout/base/mod.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export import :layout.input;
export import :layout.layout;
export import :layout.output;
export import :layout.values;
export import :layout.runningPosition;
Loading
Loading