diff --git a/src/web/vaev-base/page.h b/src/web/vaev-base/page.h new file mode 100644 index 0000000..0b2b769 --- /dev/null +++ b/src/web/vaev-base/page.h @@ -0,0 +1,59 @@ +#pragma once + +#include + +namespace Vaev { + +// https://drafts.csswg.org/css-page/#marks +enum struct PageMarks { + NONE, + CROSS, + BOX, + + _LEN, +}; + +// https://drafts.csswg.org/css-page/#margin-boxes +#define FOREACH_PAGE_MARGIN(ITER) \ + ITER(TOP, "top") \ + ITER(TOP_LEFT_CORNER, "top-left-corner") \ + ITER(TOP_LEFT, "top-left") \ + ITER(TOP_CENTER, "top-center") \ + ITER(TOP_RIGHT, "top-right") \ + ITER(TOP_RIGHT_CORNER, "top-right-corner") \ + ITER(RIGHT, "right") \ + ITER(RIGHT_TOP, "right-top") \ + ITER(RIGHT_MIDDLE, "right-middle") \ + ITER(RIGHT_BOTTOM, "right-bottom") \ + ITER(BOTTOM, "bottom") \ + ITER(BOTTOM_RIGHT_CORNER, "bottom-right-corner") \ + ITER(BOTTOM_RIGHT, "bottom-right") \ + ITER(BOTTOM_CENTER, "bottom-center") \ + ITER(BOTTOM_LEFT, "bottom-left") \ + ITER(BOTTOM_LEFT_CORNER, "bottom-left-corner") \ + ITER(LEFT, "left") \ + ITER(LEFT_BOTTOM, "left-bottom") \ + ITER(LEFT_MIDDLE, "left-middle") \ + ITER(LEFT_TOP, "left-top") + +enum struct PageMargin { +#define ITER(ID, ...) ID, + FOREACH_PAGE_MARGIN(ITER) +#undef ITER + _LEN, +}; + +// https://drafts.csswg.org/css-page/#page-orientation-prop +enum struct PageOrientation { + UPRIGHT, + ROTATE_LEFT, + ROTATE_RIGHT, + + _LEN +}; + +struct Page { + Opt margin; +}; + +} // namespace Vaev diff --git a/src/web/vaev-driver/render.cpp b/src/web/vaev-driver/render.cpp index 7dfabee..4f26b58 100644 --- a/src/web/vaev-driver/render.cpp +++ b/src/web/vaev-driver/render.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -124,32 +125,273 @@ Vec> print(Markup::Document const &dom, Style::Media const & Style::Computer computer{media, stylebook}; + Vec> pages; + + Layout::Resolver resolver{}; + auto pageStyle = computer.computeForPage(pages.len()); + RectPx pageRect{ + 0_px, + 0_px, + media.width, + media.height, + }; + auto page = makeStrong(pageRect.size().cast()); + + InsetsPx pageMargin = { + resolver.resolve(pageStyle->margin->top, pageRect.height), + resolver.resolve(pageStyle->margin->end, pageRect.width), + resolver.resolve(pageStyle->margin->bottom, pageRect.height), + resolver.resolve(pageStyle->margin->start, pageRect.width), + }; + + RectPx pageContent = pageRect.shrink(pageMargin); + + // MARK: Top Left Corner --------------------------------------------------- + + auto topLeftMarginCornerRect = RectPx::fromTwoPoint( + pageRect.topStart(), + pageContent.topStart() + ); + Layout::Tree topLeftMarginCornerTree{ + .root = Layout::buildForPseudoElement(computer.computeFor(pages.len(), PageMargin::TOP_LEFT_CORNER)), + .viewport = Layout::Viewport{.small = topLeftMarginCornerRect.size()} + }; + Layout::layout( + topLeftMarginCornerTree, + topLeftMarginCornerTree.root, + { + .commit = Layout::Commit::YES, + .knownSize = topLeftMarginCornerRect.size().cast>(), + .position = topLeftMarginCornerRect.topStart(), + .availableSpace = topLeftMarginCornerRect.size(), + .containingBlock = topLeftMarginCornerRect.size(), + } + ); + Layout::paint(topLeftMarginCornerTree.root, *page); + + // MARK: Top Right Corner -------------------------------------------------- + + auto topRightMarginCornerRect = RectPx::fromTwoPoint( + pageRect.topEnd(), + pageContent.topEnd() + ); + Layout::Tree topRightMarginCornerTree{ + .root = Layout::buildForPseudoElement(computer.computeFor(pages.len(), PageMargin::TOP_RIGHT_CORNER)), + .viewport = Layout::Viewport{.small = topRightMarginCornerRect.size()} + }; + Layout::layout( + topRightMarginCornerTree, + topRightMarginCornerTree.root, + { + .commit = Layout::Commit::YES, + .knownSize = topRightMarginCornerRect.size().cast>(), + .position = topRightMarginCornerRect.topStart(), + .availableSpace = topRightMarginCornerRect.size(), + .containingBlock = topRightMarginCornerRect.size(), + } + ); + Layout::paint(topRightMarginCornerTree.root, *page); + + // MARK: Bottom Left Corner ------------------------------------------------ + + auto bottomLeftMarginCornerRect = RectPx::fromTwoPoint( + pageRect.bottomStart(), + pageContent.bottomStart() + ); + Layout::Tree bottomLeftMarginCornerTree{ + .root = Layout::buildForPseudoElement(computer.computeFor(pages.len(), PageMargin::BOTTOM_LEFT_CORNER)), + .viewport = Layout::Viewport{.small = bottomLeftMarginCornerRect.size()} + }; + Layout::layout( + bottomLeftMarginCornerTree, + bottomLeftMarginCornerTree.root, + { + .commit = Layout::Commit::YES, + .knownSize = bottomLeftMarginCornerRect.size().cast>(), + .position = bottomLeftMarginCornerRect.topStart(), + .availableSpace = bottomLeftMarginCornerRect.size(), + .containingBlock = bottomLeftMarginCornerRect.size(), + } + ); + Layout::paint(bottomLeftMarginCornerTree.root, *page); + + // MARK: Bottom Right Corner ----------------------------------------------- + + auto bottomRightMarginCornerRect = RectPx::fromTwoPoint( + pageRect.bottomEnd(), + pageContent.bottomEnd() + ); + Layout::Tree bottomRightMarginCornerTree{ + .root = Layout::buildForPseudoElement(computer.computeFor(pages.len(), PageMargin::BOTTOM_RIGHT_CORNER)), + .viewport = Layout::Viewport{.small = bottomRightMarginCornerRect.size()} + }; + Layout::layout( + bottomRightMarginCornerTree, + bottomRightMarginCornerTree.root, + { + .commit = Layout::Commit::YES, + .knownSize = bottomRightMarginCornerRect.size().cast>(), + .position = bottomRightMarginCornerRect.topStart(), + .availableSpace = bottomRightMarginCornerRect.size(), + .containingBlock = bottomRightMarginCornerRect.size(), + } + ); + Layout::paint(bottomRightMarginCornerTree.root, *page); + + // MARK: Top --------------------------------------------------------------- + + auto topRect = RectPx::fromTwoPoint( + topLeftMarginCornerRect.topEnd(), + topRightMarginCornerRect.bottomStart() + ); + + auto topBox = Layout::buildForPseudoElement(computer.computeFor(pages.len(), PageMargin::TOP)); + topBox.add(Layout::buildForPseudoElement(computer.computeFor(pages.len(), PageMargin::TOP_LEFT))); + topBox.add(Layout::buildForPseudoElement(computer.computeFor(pages.len(), PageMargin::TOP_CENTER))); + topBox.add(Layout::buildForPseudoElement(computer.computeFor(pages.len(), PageMargin::TOP_RIGHT))); + + Layout::Tree topTree{ + .root = std::move(topBox), + .viewport = Layout::Viewport{.small = topRect.size()} + }; + + Layout::layout( + topTree, + topTree.root, + { + .commit = Layout::Commit::YES, + .knownSize = topRect.size().cast>(), + .position = topRect.topStart(), + .availableSpace = topRect.size(), + .containingBlock = topRect.size(), + } + ); + Layout::paint(topTree.root, *page); + + // MARK: Bottom ------------------------------------------------------------ + + auto bottomRect = RectPx::fromTwoPoint( + bottomLeftMarginCornerRect.topEnd(), + bottomRightMarginCornerRect.bottomStart() + ); + + auto bottomBox = Layout::buildForPseudoElement(computer.computeFor(pages.len(), PageMargin::BOTTOM)); + bottomBox.add(Layout::buildForPseudoElement(computer.computeFor(pages.len(), PageMargin::BOTTOM_LEFT))); + bottomBox.add(Layout::buildForPseudoElement(computer.computeFor(pages.len(), PageMargin::BOTTOM_CENTER))); + bottomBox.add(Layout::buildForPseudoElement(computer.computeFor(pages.len(), PageMargin::BOTTOM_RIGHT))); + + Layout::Tree bottomTree{ + .root = std::move(bottomBox), + .viewport = Layout::Viewport{.small = bottomRect.size()} + }; + + Layout::layout( + bottomTree, + bottomTree.root, + { + .commit = Layout::Commit::YES, + .knownSize = bottomRect.size().cast>(), + .position = bottomRect.topStart(), + .availableSpace = bottomRect.size(), + .containingBlock = bottomRect.size(), + } + ); + + Layout::paint(bottomTree.root, *page); + + // MARK: Left -------------------------------------------------------------- + auto leftRect = RectPx::fromTwoPoint( + topLeftMarginCornerRect.bottomEnd(), + bottomLeftMarginCornerRect.topStart() + ); + + auto leftBox = Layout::buildForPseudoElement(computer.computeFor(pages.len(), PageMargin::LEFT)); + leftBox.add(Layout::buildForPseudoElement(computer.computeFor(pages.len(), PageMargin::LEFT_TOP))); + leftBox.add(Layout::buildForPseudoElement(computer.computeFor(pages.len(), PageMargin::LEFT_MIDDLE))); + leftBox.add(Layout::buildForPseudoElement(computer.computeFor(pages.len(), PageMargin::LEFT_BOTTOM))); + + Layout::Tree leftTree{ + .root = std::move(leftBox), + .viewport = Layout::Viewport{.small = leftRect.size()} + }; + + Layout::layout( + leftTree, + leftTree.root, + { + .commit = Layout::Commit::YES, + .knownSize = leftRect.size().cast>(), + .position = leftRect.topStart(), + .availableSpace = leftRect.size(), + .containingBlock = leftRect.size(), + } + ); + + Layout::paint(leftTree.root, *page); + + // MARK: Right ------------------------------------------------------------- + + auto rightRect = RectPx::fromTwoPoint( + topRightMarginCornerRect.bottomEnd(), + bottomRightMarginCornerRect.topStart() + ); + + auto rightBox = Layout::buildForPseudoElement(computer.computeFor(pages.len(), PageMargin::RIGHT)); + rightBox.add(Layout::buildForPseudoElement(computer.computeFor(pages.len(), PageMargin::RIGHT_TOP))); + rightBox.add(Layout::buildForPseudoElement(computer.computeFor(pages.len(), PageMargin::RIGHT_MIDDLE))); + rightBox.add(Layout::buildForPseudoElement(computer.computeFor(pages.len(), PageMargin::RIGHT_BOTTOM))); + + Layout::Tree rightTree{ + .root = std::move(rightBox), + .viewport = Layout::Viewport{.small = rightRect.size()} + }; + + Layout::layout( + rightTree, + rightTree.root, + { + .commit = Layout::Commit::YES, + .knownSize = rightRect.size().cast>(), + .position = rightRect.topStart(), + .availableSpace = rightRect.size(), + .containingBlock = rightRect.size(), + } + ); + + Layout::paint(rightTree.root, *page); + + // MARK: Page Content ------------------------------------------------------ + Layout::Viewport vp{ - .small = { + .small = pageContent.size(), + .large = { + Px{media.width}, + Px{media.height}, + }, + .dynamic = { Px{media.width}, Px{media.height}, }, }; - Layout::Tree tree = { + Layout::Tree contentTree = { Layout::build(computer, dom), vp, }; Layout::layout( - tree, - tree.root, + contentTree, + contentTree.root, { .commit = Layout::Commit::YES, - .knownSize = {vp.small.width, NONE}, - .availableSpace = {vp.small.width, 0_px}, - .containingBlock = {vp.small.width, vp.small.height}, + .knownSize = {pageContent.width, NONE}, + .position = pageContent.topStart(), + .availableSpace = pageContent.size(), + .containingBlock = pageContent.size(), } ); - Vec> pages; - auto page = makeStrong(vp.small.size().cast()); - Layout::paint(tree.root, *page); + Layout::paint(contentTree.root, *page); page->prepare(); pages.pushBack(page); diff --git a/src/web/vaev-layout/builder.cpp b/src/web/vaev-layout/builder.cpp index 73c2000..e86abbd 100644 --- a/src/web/vaev-layout/builder.cpp +++ b/src/web/vaev-layout/builder.cpp @@ -282,4 +282,29 @@ Box build(Style::Computer &c, Markup::Document const &doc) { return root; } +Box buildForPseudoElement(Strong style) { + auto fontFace = _lookupFontface(*style); + + // FIXME: We should pass this around from the top in order to properly resolve rems + Resolver resolver{ + .rootFont = Text::Font{fontFace, 16}, + .boxFont = Text::Font{fontFace, 16}, + }; + Text::ProseStyle proseStyle{ + .font = { + fontFace, + resolver.resolve(style->font->size).cast(), + }, + .multiline = true, + }; + + if (style->content) { + auto prose = makeStrong(proseStyle); + prose->append(style->content.str()); + return {style, fontFace, prose}; + } else { + return {style, fontFace}; + } +} + } // namespace Vaev::Layout diff --git a/src/web/vaev-layout/builder.h b/src/web/vaev-layout/builder.h index ebcd6d2..915fa90 100644 --- a/src/web/vaev-layout/builder.h +++ b/src/web/vaev-layout/builder.h @@ -6,4 +6,6 @@ namespace Vaev::Layout { Box build(Style::Computer &c, Markup::Document const &doc); +Box buildForPseudoElement(Strong style); + } // namespace Vaev::Layout diff --git a/src/web/vaev-style/computed.h b/src/web/vaev-style/computed.h index eda4860..bb9e4bf 100644 --- a/src/web/vaev-style/computed.h +++ b/src/web/vaev-style/computed.h @@ -28,6 +28,7 @@ struct Computed { Color color; Number opacity; + String content = ""s; AlignProps aligns; Math::Vec2>> gaps; diff --git a/src/web/vaev-style/computer.cpp b/src/web/vaev-style/computer.cpp index fe1f4e3..3e7c74c 100644 --- a/src/web/vaev-style/computer.cpp +++ b/src/web/vaev-style/computer.cpp @@ -91,4 +91,131 @@ Strong Computer::computeFor(Computed const &parent, Markup::Element co return computed; } +Strong Computer::computeForPage(usize) { + auto computed = makeStrong(Computed::initial()); + + computed->margin.cow() = Margin{Length(3.0, Length::CM)}; + + return computed; +} + +Strong Computer::computeFor(usize, PageMargin margin) { + auto computed = makeStrong(Computed::initial()); + computed->backgrounds.cow().color = Gfx::PINK; + + switch (margin) { + // MARK: Top --------------------------------------------------------------- + case PageMargin::TOP: + computed->display = {Display::FLEX, Display::BLOCK}; + + break; + + case PageMargin::TOP_LEFT_CORNER: + computed->content = "Corner"s; + computed->backgrounds.cow().color = Gfx::RED; + break; + + case PageMargin::TOP_LEFT: + computed->content = "Left"s; + computed->backgrounds.cow().color = Gfx::BLUE; + break; + + case PageMargin::TOP_CENTER: + computed->content = "Center"s; + computed->backgrounds.cow().color = Gfx::GREEN; + computed->flex.cow().grow = 1; + break; + + case PageMargin::TOP_RIGHT: + computed->content = "Right"s; + computed->backgrounds.cow().color = Gfx::BLUE; + break; + + case PageMargin::TOP_RIGHT_CORNER: + computed->content = "Corner"s; + computed->backgrounds.cow().color = Gfx::RED; + break; + + // MARK: Right ------------------------------------------------------------- + case PageMargin::RIGHT: + computed->display = {Display::FLEX, Display::BLOCK}; + computed->flex.cow().direction = FlexDirection::COLUMN; + break; + + case PageMargin::RIGHT_TOP: + computed->content = "Top"s; + computed->backgrounds.cow().color = Gfx::BLUE; + break; + + case PageMargin::RIGHT_MIDDLE: + computed->content = "Middle"s; + computed->backgrounds.cow().color = Gfx::GREEN; + computed->flex.cow().grow = 1; + break; + + case PageMargin::RIGHT_BOTTOM: + computed->content = "Bottom"s; + computed->backgrounds.cow().color = Gfx::BLUE; + break; + + // MARK: Bottom ------------------------------------------------------------ + case PageMargin::BOTTOM: + computed->display = {Display::FLEX, Display::BLOCK}; + break; + + case PageMargin::BOTTOM_RIGHT_CORNER: + computed->content = "Corner"s; + computed->backgrounds.cow().color = Gfx::RED; + break; + + case PageMargin::BOTTOM_RIGHT: + computed->content = "Right"s; + computed->backgrounds.cow().color = Gfx::BLUE; + break; + + case PageMargin::BOTTOM_CENTER: + computed->content = "Center"s; + computed->backgrounds.cow().color = Gfx::GREEN; + computed->flex.cow().grow = 1; + break; + + case PageMargin::BOTTOM_LEFT: + computed->content = "Left"s; + computed->backgrounds.cow().color = Gfx::BLUE; + break; + + case PageMargin::BOTTOM_LEFT_CORNER: + computed->content = "Corner"s; + computed->backgrounds.cow().color = Gfx::RED; + break; + + // MARK: Left -------------------------------------------------------------- + case PageMargin::LEFT: + computed->display = {Display::FLEX, Display::BLOCK}; + computed->flex.cow().direction = FlexDirection::COLUMN; + break; + + case PageMargin::LEFT_BOTTOM: + computed->content = "Bottom"s; + computed->backgrounds.cow().color = Gfx::BLUE; + break; + + case PageMargin::LEFT_MIDDLE: + computed->content = "Middle"s; + computed->backgrounds.cow().color = Gfx::GREEN; + computed->flex.cow().grow = 1; + break; + + case PageMargin::LEFT_TOP: + computed->content = "Top"s; + computed->backgrounds.cow().color = Gfx::BLUE; + break; + + default: + unreachable(); + } + + return computed; +} + } // namespace Vaev::Style diff --git a/src/web/vaev-style/computer.h b/src/web/vaev-style/computer.h index 5ea944b..4175362 100644 --- a/src/web/vaev-style/computer.h +++ b/src/web/vaev-style/computer.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include "computed.h" @@ -16,6 +17,10 @@ struct Computer { void _evalRule(Rule const &rule, Markup::Element const &el, MatchingRules &matches); Strong computeFor(Computed const &parent, Markup::Element const &el); + + Strong computeForPage(usize pageIndex); + + Strong computeFor(usize pageIndex, PageMargin margin); }; } // namespace Vaev::Style