From 1c6eaea3d22faa50ea54872a6b455722f0912316 Mon Sep 17 00:00:00 2001 From: VAN BOSSUYT Nicolas Date: Thu, 12 Dec 2024 08:30:45 +0100 Subject: [PATCH] vaev-driver: Implement orientation and margins print settings. --- src/libs/karm-io/fmt.h | 6 +- src/libs/karm-kira/print-dialog.cpp | 132 ++++++++--- src/libs/karm-print/paper.h | 14 +- src/main.cpp | 120 +++++----- src/web/vaev-base/media.h | 7 - src/web/vaev-driver/fetcher.cpp | 36 +++ src/web/vaev-driver/fetcher.h | 2 + src/web/vaev-driver/print.cpp | 353 ++++++++++++++++++++++++++++ src/web/vaev-driver/print.h | 11 + src/web/vaev-driver/render.cpp | 318 +------------------------ src/web/vaev-driver/render.h | 2 - src/web/vaev-driver/res/print.css | 4 +- src/web/vaev-layout/builder.cpp | 1 - src/web/vaev-style/computed.h | 11 + src/web/vaev-style/computer.cpp | 4 +- src/web/vaev-style/computer.h | 2 +- src/web/vaev-style/media.h | 5 +- src/web/vaev-style/page.h | 18 +- src/web/vaev-style/styles.cpp | 2 +- src/web/vaev-style/styles.h | 2 +- src/web/vaev-style/values.cpp | 6 +- src/web/vaev-style/values.h | 5 +- src/web/vaev-view/dialog.cpp | 38 +-- src/web/vaev-view/view.cpp | 2 +- 24 files changed, 621 insertions(+), 480 deletions(-) create mode 100644 src/web/vaev-driver/print.cpp create mode 100644 src/web/vaev-driver/print.h diff --git a/src/libs/karm-io/fmt.h b/src/libs/karm-io/fmt.h index 40a65df..ed64ceb 100644 --- a/src/libs/karm-io/fmt.h +++ b/src/libs/karm-io/fmt.h @@ -277,8 +277,10 @@ struct Formatter> { } Res format(Io::TextWriter &writer, Cased val) { - auto result = try$(changeCase(val._inner, val._case)); - return writer.writeStr(result); + Io::StringWriter sw; + try$(_innerFmt.format(sw, val._inner)); + String result = try$(changeCase(sw.str(), val._case)); + return writer.writeStr(result.str()); } }; diff --git a/src/libs/karm-kira/print-dialog.cpp b/src/libs/karm-kira/print-dialog.cpp index c308613..b926b08 100644 --- a/src/libs/karm-kira/print-dialog.cpp +++ b/src/libs/karm-kira/print-dialog.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -20,9 +21,55 @@ struct State { Vec> pages = preview(settings); }; -using Action = Union; +struct ChangePaper { + Print::PaperStock paper; +}; + +struct ChangeOrientation { + Print::Orientation orientation; +}; + +struct ChangeMargin { + Print::Margins margins; +}; + +struct ToggleHeaderFooter {}; + +struct ToggleBackgroundGraphics {}; + +using Action = Union< + ChangePaper, + ChangeOrientation, + ChangeMargin, + ToggleHeaderFooter, + ToggleBackgroundGraphics>; + +static void reduce(State &s, Action a) { + bool shouldUpdatePreview = false; + + if (auto changePaper = a.is()) { + s.settings.paper = changePaper->paper; + shouldUpdatePreview = true; + } else if (auto changeOrientation = a.is()) { + s.settings.orientation = changeOrientation->orientation; + shouldUpdatePreview = true; + } else if (auto changeMargin = a.is()) { + s.settings.margins = changeMargin->margins; + shouldUpdatePreview = true; + } else if (a.is()) { + s.settings.headerFooter = not s.settings.headerFooter; + shouldUpdatePreview = true; + } else if (a.is()) { + s.settings.backgroundGraphics = not s.settings.backgroundGraphics; + shouldUpdatePreview = true; + } -void reduce(State &, Action) { + if (shouldUpdatePreview) { + auto settings = s.settings; + if (settings.orientation == Print::Orientation::LANDSCAPE) + settings.paper = s.settings.paper.landscape(); + s.pages = s.preview(settings); + } } using Model = Ui::Model; @@ -48,21 +95,34 @@ Ui::Child _printSelect(State const &s, usize index) { Ui::Child _printPaper(State const &s, usize index) { auto scale = 1.; + + auto paper = s.settings.paper; + if (s.settings.orientation == Print::Orientation::LANDSCAPE) + paper = paper.landscape(); + auto isMobile = App::useFormFactor() == App::FormFactor::MOBILE; - if (isMobile) { + if (isMobile) scale = 0.5; - } + + Math::Vec2f previewSize{ + 320 * scale, + 320 * (1. / paper.aspect()) * scale, + }; return Ui::stack( - Ui::canvas(s.pages[index]) | - Ui::box({ + Ui::canvas( + s.pages[index], + { + .showBackgroundGraphics = s.settings.backgroundGraphics, + } + ) | Ui::box({ .borderWidth = 1, .borderFill = Ui::GRAY50.withOpacity(0.1), .backgroundFill = Gfx::WHITE, }), _printSelect(s, index) | Ui::align(Math::Align::BOTTOM_END) ) | - Ui::pinSize(Math::Vec2f{320 * scale, 452 * scale}.cast()); + Ui::pinSize(previewSize.cast()); } Ui::Child _printPreviewMobile(State const &s) { @@ -129,8 +189,8 @@ Ui::Child _destinationSelect() { ); } -Ui::Child _paperSelect() { - return select(selectValue("A4"s), [] -> Ui::Children { +Ui::Child _paperSelect(State const &s) { + return select(selectValue(s.settings.paper.name), [] -> Ui::Children { Vec groups; bool first = false; @@ -138,7 +198,7 @@ Ui::Child _paperSelect() { Vec items; items.pushBack(selectLabel(serie.name)); for (auto const &stock : serie.stocks) { - items.pushBack(selectItem(Ui::NOP, stock.name)); + items.pushBack(selectItem(Model::bind(stock), stock.name)); } if (not first) @@ -152,7 +212,7 @@ Ui::Child _paperSelect() { }); } -Ui::Child _printSettings() { +Ui::Child _printSettings(State const &s) { return Ui::vflow( rowContent( NONE, @@ -187,11 +247,15 @@ Ui::Child _printSettings() { "Pages"s ), selectRow( - selectValue("Portrait"s), + selectValue( + s.settings.orientation == Print::Orientation::PORTRAIT + ? "Portrait"s + : "Landscape"s + ), [] -> Ui::Children { return { - selectItem(Ui::NOP, "Portrait"s), - selectItem(Ui::NOP, "Landscape"s), + selectItem(Model::bind(Print::Orientation::PORTRAIT), "Portrait"s), + selectItem(Model::bind(Print::Orientation::LANDSCAPE), "Landscape"s), }; }, @@ -202,13 +266,13 @@ Ui::Child _printSettings() { NONE, "More settings"s, NONE, - Karm::Ui::Slots{[] -> Ui::Children { + Karm::Ui::Slots{[&] -> Ui::Children { return { rowContent( NONE, "Paper"s, NONE, - _paperSelect() + _paperSelect(s) ), selectRow( selectValue("1"s), @@ -225,28 +289,40 @@ Ui::Child _printSettings() { "Page per sheet"s ), selectRow( - selectValue("Default"s), + selectValue(Io::format("{}", Io::cased(s.settings.margins, Io::Case::CAPITAL)).unwrap()), [] -> Ui::Children { return { - selectItem(Ui::NOP, "None"s), - selectItem(Ui::NOP, "Small"s), - selectItem(Ui::NOP, "Default"s), - selectItem(Ui::NOP, "Large"s), + selectItem(Model::bind(Print::Margins::NONE), "None"s), + selectItem(Model::bind(Print::Margins::MINIMUM), "Minimum"s), + selectItem(Model::bind(Print::Margins::DEFAULT), "Default"s), + selectItem(Model::bind(Print::Margins::CUSTOM), "Custom"s), }; }, "Margins"s ), - checkboxRow(true, NONE, "Header and footers"s), - checkboxRow(false, NONE, "Background graphics"s), + checkboxRow( + s.settings.headerFooter, + [&](auto &n, ...) { + Model::bubble(n); + }, + "Header and footers"s + ), + checkboxRow( + s.settings.backgroundGraphics, + [&](auto &n, ...) { + Model::bubble(n); + }, + "Background graphics"s + ), }; }} ) ); } -Ui::Child _printControls() { - return _printSettings() | +Ui::Child _printControls(State const &s) { + return _printSettings(s) | Ui::vscroll() | Ui::grow() | Ui::minSize({320, Ui::UNCONSTRAINED}); @@ -257,7 +333,7 @@ Ui::Child _printDialog(State const &s) { dialogTitleBar("Print"s), Ui::hflow( _printPreview(s), - _printControls() | Ui::grow() + _printControls(s) | Ui::grow() ) | Ui::maxSize({Ui::UNCONSTRAINED, 500}) | Ui::grow(), Ui::separator(), @@ -276,7 +352,7 @@ Ui::Child _printDialogMobile(State const &s) { _printPreviewMobile(s), Ui::separator(), titleRow("Settings"s), - _printSettings() + _printSettings(s) ) | Ui::minSize(500) | Ui::vscroll() | Ui::grow(), @@ -293,7 +369,7 @@ Ui::Child printDialog(PrintPreview preview) { auto isMobile = App::useFormFactor() == App::FormFactor::MOBILE; if (isMobile) return _printDialogMobile(s); - return _printDialog(s); + return _printDialog(s) | Ui::popoverLayer(); }); } diff --git a/src/libs/karm-print/paper.h b/src/libs/karm-print/paper.h index 4c37a8f..4cc383e 100644 --- a/src/libs/karm-print/paper.h +++ b/src/libs/karm-print/paper.h @@ -130,10 +130,12 @@ struct Margins { NONE, MINIMUM, CUSTOM, + + _LEN }; using enum _Named; _Named named; - Math::Insetsf custom; + Math::Insetsf custom = 20 * UNIT; Margins(_Named named) : named(named) {} @@ -142,11 +144,21 @@ struct Margins { bool operator==(_Named named) const { return this->named == named; } + + void repr(Io::Emit &e) const { + e("{}", named); + } +}; + +enum struct Orientation { + PORTRAIT, + LANDSCAPE }; struct Settings { PaperStock paper = Print::A4; Margins margins = Margins::DEFAULT; + Orientation orientation = Orientation::PORTRAIT; double scale = 1.; bool headerFooter = true; diff --git a/src/main.cpp b/src/main.cpp index 9ca2517..0d2dcf0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,13 +3,14 @@ #include #include #include -#include -#include +#include +#include #include #include #include #include #include +#include #include #include #include @@ -100,41 +101,6 @@ Res<> markupDumpTokens(Mime::Url const &url) { return Ok(); } -Vaev::Style::Media constructMediaForPrint(Resolution resolution, Vec2Px paperSize) { - return { - .type = Vaev::MediaType::PRINT, - - .width = paperSize.width, - .height = paperSize.height, - .aspectRatio = (double)paperSize.width / (double)paperSize.height, - .orientation = Vaev::Orientation::PORTRAIT, - - .resolution = resolution, - .scan = Vaev::Scan::PROGRESSIVE, - .grid = false, - .update = Vaev::Update::NONE, - - .overflowBlock = Vaev::OverflowBlock::PAGED, - .overflowInline = Vaev::OverflowInline::NONE, - - .color = 8, - .colorIndex = 0, - .monochrome = 0, - .colorGamut = Vaev::ColorGamut::SRGB, - .pointer = Vaev::Pointer::NONE, - .hover = Vaev::Hover::NONE, - .anyPointer = Vaev::Pointer::NONE, - .anyHover = Vaev::Hover::NONE, - - .prefersReducedMotion = Vaev::ReducedMotion::REDUCE, - .prefersReducedTransparency = Vaev::ReducedTransparency::REDUCE, - .prefersContrast = Vaev::Contrast::MORE, - .forcedColors = Vaev::Colors::NONE, - .prefersColorScheme = Vaev::ColorScheme::LIGHT, - .prefersReducedData = Vaev::ReducedData::NO_PREFERENCE, - }; -} - struct PrintOption { bool dumpStyle = false; bool dumpDom = false; @@ -142,23 +108,41 @@ struct PrintOption { bool dumpPaint = false; bool printToBMP = false; - Resolution resolution = Resolution::fromDpi(96); + Resolution scale = Resolution::fromDppx(1); Opt width = NONE; Opt height = NONE; Print::PaperStock paper = Print::A4; + Print::Orientation orientation = Print::Orientation::PORTRAIT; }; Res<> print(Mime::Url const &, Strong dom, Io::Writer &output, PrintOption options = {}) { + Layout::Resolver resolver; + resolver.viewport.dpi = options.scale; - Vec2Px paperSize = { - Px{options.paper.width}, - Px{options.paper.height}, - }; + auto paper = options.paper; + + if (options.orientation == Print::Orientation::LANDSCAPE) + paper = paper.landscape(); + + if (options.width) { + paper.name = "custom"; + paper.width = resolver.resolve(*options.width).cast(); + } - auto media = constructMediaForPrint(options.resolution, paperSize); + if (options.height) { + paper.name = "custom"; + paper.height = resolver.resolve(*options.height).cast(); + } + + Print::Settings settings = { + .paper = paper, + .scale = options.scale.toDppx(), + .headerFooter = true, + .backgroundGraphics = false, + }; auto pages = Vaev::Driver::print( *dom, - media + settings ); if (options.dumpDom) @@ -169,16 +153,19 @@ Res<> print(Mime::Url const &, Strong dom, Io::Writer &output, Strong printer = options.printToBMP - ? Strong{makeStrong(Print::BMPPrinter{options.paper})} - : makeStrong(Print::PdfPrinter{options.paper}); + ? Strong{makeStrong()} + : makeStrong(); for (auto &page : pages) { - page->print(*printer); + page->print( + *printer, + { + .showBackgroundGraphics = true, + } + ); } - printer->write(output); - - return Ok(); + return printer->write(output); } Res<> print(Mime::Url const &url, Io::Reader &input, Io::Writer &output, PrintOption options = {}) { @@ -191,15 +178,15 @@ Res<> print(Mime::Url const &url, Io::Writer &output, PrintOption options = {}) return print(url, dom, output, options); } -Vaev::Style::Media constructMediaForRender(Resolution resolution, Vec2Px size) { +Vaev::Style::Media constructMediaForRender(Resolution scale, Vec2Px size) { return { .type = Vaev::MediaType::SCREEN, .width = size.width, .height = size.height, .aspectRatio = (Number)size.width / (Number)size.height, - .orientation = Vaev::Orientation::PORTRAIT, + .orientation = Print::Orientation::PORTRAIT, - .resolution = resolution, + .resolution = scale, .scan = Vaev::Scan::PROGRESSIVE, .grid = false, .update = Vaev::Update::NONE, @@ -385,22 +372,23 @@ Async::Task<> entryPointAsync(Sys::Context &ctx) { Cli::Option widthArg = Cli::option('w', "width"s, "Width of the output document in css units (e.g. 800px)"s, ""s); Cli::Option heightArg = Cli::option('h', "height"s, "Height of the output document in css units (e.g. 600px)"s, ""s); Cli::Option paperArg = Cli::option(NONE, "paper"s, "Paper size for printing (default: A4)"s, "A4"s); + Cli::Option orientationArg = Cli::option(NONE, "orientation"s, "Page orientation (default: portrait)"s, "portrait"s); cmd.subCommand( "print"s, 'p', "Render document for printing"s, - { - inputArg, - outputArg, - dumpStyleArg, - dumpDomArg, - dumpLayoutArg, - dumpPaintArg, - scaleArg, - widthArg, - heightArg, - paperArg, + {inputArg, + outputArg, + dumpStyleArg, + dumpDomArg, + dumpLayoutArg, + dumpPaintArg, + scaleArg, + widthArg, + heightArg, + paperArg, + orientationArg }, [=](Sys::Context &) -> Async::Task<> { Vaev::Tools::PrintOption options{ @@ -410,7 +398,7 @@ Async::Task<> entryPointAsync(Sys::Context &ctx) { .dumpPaint = dumpPaintArg, }; - options.resolution = co_try$(Vaev::Style::parseValue(scaleArg.unwrap())); + options.scale = co_try$(Vaev::Style::parseValue(scaleArg.unwrap())); if (widthArg.unwrap()) options.width = co_try$(Vaev::Style::parseValue(widthArg.unwrap())); @@ -420,6 +408,8 @@ Async::Task<> entryPointAsync(Sys::Context &ctx) { options.paper = co_try$(Print::findPaperStock(paperArg.unwrap())); + options.orientation = co_try$(Vaev::Style::parseValue(orientationArg.unwrap())); + Mime::Url inputUrl = "about:stdin"_url; MutCursor input = &Sys::in(); MutCursor output = &Sys::out(); diff --git a/src/web/vaev-base/media.h b/src/web/vaev-base/media.h index 7107e83..38794ba 100644 --- a/src/web/vaev-base/media.h +++ b/src/web/vaev-base/media.h @@ -14,13 +14,6 @@ enum struct MediaType { _LEN, }; -enum struct Orientation { - PORTRAIT, - LANDSCAPE, - - _LEN, -}; - enum struct Scan { INTERLACE, PROGRESSIVE, diff --git a/src/web/vaev-driver/fetcher.cpp b/src/web/vaev-driver/fetcher.cpp index 7e88ebf..6033041 100644 --- a/src/web/vaev-driver/fetcher.cpp +++ b/src/web/vaev-driver/fetcher.cpp @@ -122,4 +122,40 @@ Res fetchStylesheet(Mime::Url url, Style::Origin origin) { return Ok(Style::StyleSheet::parse(s, origin)); } +void fetchStylesheets(Markup::Node const &node, Style::StyleBook &sb) { + auto el = node.is(); + if (el and el->tagName == Html::STYLE) { + auto text = el->textContent(); + Io::SScan textScan{text}; + auto sheet = Style::StyleSheet::parse(textScan); + sb.add(std::move(sheet)); + } else if (el and el->tagName == Html::LINK) { + auto rel = el->getAttribute(Html::REL_ATTR); + if (rel == "stylesheet"s) { + auto href = el->getAttribute(Html::HREF_ATTR); + if (not href) { + logWarn("link element missing href attribute"); + return; + } + + auto url = Mime::parseUrlOrPath(*href); + if (not url) { + logWarn("link element href attribute is not a valid URL: {}", *href); + return; + } + + auto sheet = fetchStylesheet(url.take(), Style::Origin::AUTHOR); + if (not sheet) { + logWarn("failed to fetch stylesheet: {}", sheet); + return; + } + + sb.add(sheet.take()); + } + } else { + for (auto &child : node.children()) + fetchStylesheets(*child, sb); + } +} + } // namespace Vaev::Driver diff --git a/src/web/vaev-driver/fetcher.h b/src/web/vaev-driver/fetcher.h index a66703b..777bc5b 100644 --- a/src/web/vaev-driver/fetcher.h +++ b/src/web/vaev-driver/fetcher.h @@ -8,6 +8,8 @@ namespace Vaev::Driver { Res fetchStylesheet(Mime::Url url, Style::Origin origin = Style::Origin::AUTHOR); +void fetchStylesheets(Markup::Node const &node, Style::StyleBook &sb); + Res> fetchDocument(Mime::Url const &url); Res> loadDocument(Mime::Url const &url, Mime::Mime const &mime, Io::Reader &reader); diff --git a/src/web/vaev-driver/print.cpp b/src/web/vaev-driver/print.cpp new file mode 100644 index 0000000..a54c85a --- /dev/null +++ b/src/web/vaev-driver/print.cpp @@ -0,0 +1,353 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "fetcher.h" +#include "print.h" + +namespace Vaev::Driver { + +static void _paintMargins(Style::PageComputedStyle &pageStyle, RectPx pageRect, RectPx pageContent, Scene::Stack &stack) { + // MARK: Top Left Corner --------------------------------------------------- + + auto topLeftMarginCornerRect = RectPx::fromTwoPoint( + pageRect.topStart(), + pageContent.topStart() + ); + Layout::Tree topLeftMarginCornerTree{ + .root = Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::TOP_LEFT_CORNER)), + .viewport = Layout::Viewport{.small = topLeftMarginCornerRect.size()} + }; + Layout::layout( + topLeftMarginCornerTree, + { + .commit = Layout::Commit::YES, + .knownSize = topLeftMarginCornerRect.size().cast>(), + .position = topLeftMarginCornerRect.topStart(), + .availableSpace = topLeftMarginCornerRect.size(), + .containingBlock = topLeftMarginCornerRect.size(), + } + ); + Layout::paint(topLeftMarginCornerTree.root, stack); + + // MARK: Top Right Corner -------------------------------------------------- + + auto topRightMarginCornerRect = RectPx::fromTwoPoint( + pageRect.topEnd(), + pageContent.topEnd() + ); + Layout::Tree topRightMarginCornerTree{ + .root = Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::TOP_RIGHT_CORNER)), + .viewport = Layout::Viewport{.small = topRightMarginCornerRect.size()} + }; + Layout::layout( + topRightMarginCornerTree, + { + .commit = Layout::Commit::YES, + .knownSize = topRightMarginCornerRect.size().cast>(), + .position = topRightMarginCornerRect.topStart(), + .availableSpace = topRightMarginCornerRect.size(), + .containingBlock = topRightMarginCornerRect.size(), + } + ); + Layout::paint(topRightMarginCornerTree.root, stack); + + // MARK: Bottom Left Corner ------------------------------------------------ + + auto bottomLeftMarginCornerRect = RectPx::fromTwoPoint( + pageRect.bottomStart(), + pageContent.bottomStart() + ); + Layout::Tree bottomLeftMarginCornerTree{ + .root = Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::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, stack); + + // MARK: Bottom Right Corner ----------------------------------------------- + + auto bottomRightMarginCornerRect = RectPx::fromTwoPoint( + pageRect.bottomEnd(), + pageContent.bottomEnd() + ); + Layout::Tree bottomRightMarginCornerTree{ + .root = Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::BOTTOM_RIGHT_CORNER)), + .viewport = Layout::Viewport{.small = bottomRightMarginCornerRect.size()} + }; + Layout::layout( + bottomRightMarginCornerTree, + { + .commit = Layout::Commit::YES, + .knownSize = bottomRightMarginCornerRect.size().cast>(), + .position = bottomRightMarginCornerRect.topStart(), + .availableSpace = bottomRightMarginCornerRect.size(), + .containingBlock = bottomRightMarginCornerRect.size(), + } + ); + Layout::paint(bottomRightMarginCornerTree.root, stack); + + // MARK: Top --------------------------------------------------------------- + + auto topRect = RectPx::fromTwoPoint( + topLeftMarginCornerRect.topEnd(), + topRightMarginCornerRect.bottomStart() + ); + + auto topBox = Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::TOP)); + topBox.add(Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::TOP_LEFT))); + topBox.add(Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::TOP_CENTER))); + topBox.add(Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::TOP_RIGHT))); + + Layout::Tree topTree{ + .root = std::move(topBox), + .viewport = Layout::Viewport{.small = topRect.size()} + }; + + Layout::layout( + topTree, + { + .commit = Layout::Commit::YES, + .knownSize = topRect.size().cast>(), + .position = topRect.topStart(), + .availableSpace = topRect.size(), + .containingBlock = topRect.size(), + } + ); + Layout::paint(topTree.root, stack); + + // MARK: Bottom ------------------------------------------------------------ + + auto bottomRect = RectPx::fromTwoPoint( + bottomLeftMarginCornerRect.topEnd(), + bottomRightMarginCornerRect.bottomStart() + ); + + auto bottomBox = Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::BOTTOM)); + bottomBox.add(Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::BOTTOM_LEFT))); + bottomBox.add(Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::BOTTOM_CENTER))); + bottomBox.add(Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::BOTTOM_RIGHT))); + + Layout::Tree bottomTree{ + .root = std::move(bottomBox), + .viewport = Layout::Viewport{.small = bottomRect.size()} + }; + + Layout::layout( + bottomTree, + { + .commit = Layout::Commit::YES, + .knownSize = bottomRect.size().cast>(), + .position = bottomRect.topStart(), + .availableSpace = bottomRect.size(), + .containingBlock = bottomRect.size(), + } + ); + + Layout::paint(bottomTree.root, stack); + + // MARK: Left -------------------------------------------------------------- + auto leftRect = RectPx::fromTwoPoint( + topLeftMarginCornerRect.bottomEnd(), + bottomLeftMarginCornerRect.topStart() + ); + + auto leftBox = Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::LEFT)); + leftBox.add(Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::LEFT_TOP))); + leftBox.add(Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::LEFT_MIDDLE))); + leftBox.add(Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::LEFT_BOTTOM))); + + Layout::Tree leftTree{ + .root = std::move(leftBox), + .viewport = Layout::Viewport{.small = leftRect.size()} + }; + + Layout::layout( + leftTree, + { + .commit = Layout::Commit::YES, + .knownSize = leftRect.size().cast>(), + .position = leftRect.topStart(), + .availableSpace = leftRect.size(), + .containingBlock = leftRect.size(), + } + ); + + Layout::paint(leftTree.root, stack); + + // MARK: Right ------------------------------------------------------------- + + auto rightRect = RectPx::fromTwoPoint( + topRightMarginCornerRect.bottomEnd(), + bottomRightMarginCornerRect.topStart() + ); + + auto rightBox = Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::RIGHT)); + rightBox.add(Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::RIGHT_TOP))); + rightBox.add(Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::RIGHT_MIDDLE))); + rightBox.add(Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::RIGHT_BOTTOM))); + + Layout::Tree rightTree{ + .root = std::move(rightBox), + .viewport = Layout::Viewport{.small = rightRect.size()} + }; + + Layout::layout( + rightTree, + { + .commit = Layout::Commit::YES, + .knownSize = rightRect.size().cast>(), + .position = rightRect.topStart(), + .availableSpace = rightRect.size(), + .containingBlock = rightRect.size(), + } + ); + + Layout::paint(rightTree.root, stack); +} + +static Style::Media _constructMedia(Print::Settings const &settings) { + return { + .type = MediaType::SCREEN, + .width = Px{settings.paper.width}, + .height = Px{settings.paper.height}, + .aspectRatio = settings.paper.width / (f64)settings.paper.height, + .orientation = settings.orientation, + + .resolution = Resolution{settings.scale, Resolution::X}, + .scan = Scan::PROGRESSIVE, + .grid = false, + .update = Update::FAST, + + .overflowBlock = OverflowBlock::SCROLL, + .overflowInline = OverflowInline::SCROLL, + + .color = 8, + .colorIndex = 0, + .monochrome = 0, + .colorGamut = ColorGamut::SRGB, + .pointer = Pointer::FINE, + .hover = Hover::HOVER, + .anyPointer = Pointer::FINE, + .anyHover = Hover::HOVER, + + .prefersReducedMotion = ReducedMotion::NO_PREFERENCE, + .prefersReducedTransparency = ReducedTransparency::NO_PREFERENCE, + .prefersContrast = Contrast::NO_PREFERENCE, + .forcedColors = Colors::NONE, + .prefersColorScheme = ColorScheme::LIGHT, + .prefersReducedData = ReducedData::NO_PREFERENCE, + }; +} + +Vec> print(Markup::Document const &dom, Print::Settings const &settings) { + auto media = _constructMedia(settings); + + Style::StyleBook stylebook; + stylebook.add( + fetchStylesheet("bundle://vaev-driver/html.css"_url, Style::Origin::USER_AGENT) + .take("user agent stylesheet not available") + ); + stylebook.add( + fetchStylesheet("bundle://vaev-driver/print.css"_url, Style::Origin::USER_AGENT) + .take("print stylesheet not available") + ); + + fetchStylesheets(dom, stylebook); + + Style::Computer computer{ + media, stylebook + }; + + // MARK: Page and Margins -------------------------------------------------- + + Vec> pages; + + Layout::Resolver resolver{}; + Style::Page page{.name = ""s, .number = pages.len(), .blank = false}; + Style::Computed initialStyle = Style::Computed::initial(); + initialStyle.color = Gfx::BLACK; + initialStyle.setCustomProp("-vaev-url", {Css::Token::string(Io::format("\"{}\"", dom.url()).unwrap())}); + initialStyle.setCustomProp("-vaev-title", {Css::Token::string(Io::format("\"{}\"", dom.title()).unwrap())}); + initialStyle.setCustomProp("-vaev-datetime", {Css::Token::string(Io::format("\"{}\"", Sys::now()).unwrap())}); + + auto pageStyle = computer.computeFor(initialStyle, page); + RectPx pageRect{ + media.width / Px{media.resolution.toDppx()}, + media.height / Px{media.resolution.toDppx()} + }; + + auto scaleMatrix = Math::Trans2f::makeScale(media.resolution.toDppx()); + auto pageSize = pageRect.size().cast(); + auto pageScene = makeStrong( + Print::PaperStock{ + "custom"s, + pageSize.width, + pageSize.height, + }, + scaleMatrix + ); + + InsetsPx pageMargin = {}; + + if (settings.margins == Print::Margins::DEFAULT) { + 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(); + } else if (settings.margins == Print::Margins::MINIMUM) { + } + + RectPx pageContent = pageRect.shrink(pageMargin); + + if (settings.headerFooter and settings.margins != Print::Margins::NONE) + _paintMargins(*pageStyle, pageRect, pageContent, *pageScene); + + // MARK: Page Content ------------------------------------------------------ + + Layout::Viewport vp{ + .small = pageContent.size(), + }; + + Layout::Tree contentTree = { + Layout::build(computer, dom), + vp, + }; + + Layout::layout( + contentTree, + { + .commit = Layout::Commit::YES, + .knownSize = {pageContent.width, NONE}, + .position = pageContent.topStart(), + .availableSpace = pageContent.size(), + .containingBlock = pageContent.size(), + } + ); + + Layout::paint(contentTree.root, *pageScene); + pageScene->prepare(); + pages.pushBack(pageScene); + + return pages; +} + +} // namespace Vaev::Driver diff --git a/src/web/vaev-driver/print.h b/src/web/vaev-driver/print.h new file mode 100644 index 0000000..48f172d --- /dev/null +++ b/src/web/vaev-driver/print.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include +#include + +namespace Vaev::Driver { + +Vec> print(Markup::Document const &dom, Print::Settings const &settings); + +} // namespace Vaev::Driver diff --git a/src/web/vaev-driver/render.cpp b/src/web/vaev-driver/render.cpp index de8a1fb..384af47 100644 --- a/src/web/vaev-driver/render.cpp +++ b/src/web/vaev-driver/render.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -16,42 +15,6 @@ namespace Vaev::Driver { static constexpr bool DEBUG_RENDER = false; -static void _collectStyle(Markup::Node const &node, Style::StyleBook &sb) { - auto el = node.is(); - if (el and el->tagName == Html::STYLE) { - auto text = el->textContent(); - Io::SScan textScan{text}; - auto sheet = Style::StyleSheet::parse(textScan); - sb.add(std::move(sheet)); - } else if (el and el->tagName == Html::LINK) { - auto rel = el->getAttribute(Html::REL_ATTR); - if (rel == "stylesheet"s) { - auto href = el->getAttribute(Html::HREF_ATTR); - if (not href) { - logWarn("link element missing href attribute"); - return; - } - - auto url = Mime::parseUrlOrPath(*href); - if (not url) { - logWarn("link element href attribute is not a valid URL: {}", *href); - return; - } - - auto sheet = fetchStylesheet(url.take(), Style::Origin::AUTHOR); - if (not sheet) { - logWarn("failed to fetch stylesheet: {}", sheet); - return; - } - - sb.add(sheet.take()); - } - } else { - for (auto &child : node.children()) - _collectStyle(*child, sb); - } -} - RenderResult render(Markup::Document const &dom, Style::Media const &media, Layout::Viewport viewport) { Style::StyleBook stylebook; stylebook.add( @@ -60,7 +23,7 @@ RenderResult render(Markup::Document const &dom, Style::Media const &media, Layo ); auto start = Sys::now(); - _collectStyle(dom, stylebook); + fetchStylesheets(dom, stylebook); auto elapsed = Sys::now() - start; logDebugIf(DEBUG_RENDER, "style collection time: {}", elapsed); @@ -108,283 +71,4 @@ RenderResult render(Markup::Document const &dom, Style::Media const &media, Layo }; } -static void _paintMargins(Style::PageComputedStyle &pageStyle, RectPx pageRect, RectPx pageContent, Scene::Stack &stack) { - // MARK: Top Left Corner --------------------------------------------------- - - auto topLeftMarginCornerRect = RectPx::fromTwoPoint( - pageRect.topStart(), - pageContent.topStart() - ); - Layout::Tree topLeftMarginCornerTree{ - .root = Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::TOP_LEFT_CORNER)), - .viewport = Layout::Viewport{.small = topLeftMarginCornerRect.size()} - }; - Layout::layout( - topLeftMarginCornerTree, - { - .commit = Layout::Commit::YES, - .knownSize = topLeftMarginCornerRect.size().cast>(), - .position = topLeftMarginCornerRect.topStart(), - .availableSpace = topLeftMarginCornerRect.size(), - .containingBlock = topLeftMarginCornerRect.size(), - } - ); - Layout::paint(topLeftMarginCornerTree.root, stack); - - // MARK: Top Right Corner -------------------------------------------------- - - auto topRightMarginCornerRect = RectPx::fromTwoPoint( - pageRect.topEnd(), - pageContent.topEnd() - ); - Layout::Tree topRightMarginCornerTree{ - .root = Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::TOP_RIGHT_CORNER)), - .viewport = Layout::Viewport{.small = topRightMarginCornerRect.size()} - }; - Layout::layout( - topRightMarginCornerTree, - { - .commit = Layout::Commit::YES, - .knownSize = topRightMarginCornerRect.size().cast>(), - .position = topRightMarginCornerRect.topStart(), - .availableSpace = topRightMarginCornerRect.size(), - .containingBlock = topRightMarginCornerRect.size(), - } - ); - Layout::paint(topRightMarginCornerTree.root, stack); - - // MARK: Bottom Left Corner ------------------------------------------------ - - auto bottomLeftMarginCornerRect = RectPx::fromTwoPoint( - pageRect.bottomStart(), - pageContent.bottomStart() - ); - Layout::Tree bottomLeftMarginCornerTree{ - .root = Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::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, stack); - - // MARK: Bottom Right Corner ----------------------------------------------- - - auto bottomRightMarginCornerRect = RectPx::fromTwoPoint( - pageRect.bottomEnd(), - pageContent.bottomEnd() - ); - Layout::Tree bottomRightMarginCornerTree{ - .root = Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::BOTTOM_RIGHT_CORNER)), - .viewport = Layout::Viewport{.small = bottomRightMarginCornerRect.size()} - }; - Layout::layout( - bottomRightMarginCornerTree, - { - .commit = Layout::Commit::YES, - .knownSize = bottomRightMarginCornerRect.size().cast>(), - .position = bottomRightMarginCornerRect.topStart(), - .availableSpace = bottomRightMarginCornerRect.size(), - .containingBlock = bottomRightMarginCornerRect.size(), - } - ); - Layout::paint(bottomRightMarginCornerTree.root, stack); - - // MARK: Top --------------------------------------------------------------- - - auto topRect = RectPx::fromTwoPoint( - topLeftMarginCornerRect.topEnd(), - topRightMarginCornerRect.bottomStart() - ); - - auto topBox = Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::TOP)); - topBox.add(Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::TOP_LEFT))); - topBox.add(Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::TOP_CENTER))); - topBox.add(Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::TOP_RIGHT))); - - Layout::Tree topTree{ - .root = std::move(topBox), - .viewport = Layout::Viewport{.small = topRect.size()} - }; - - Layout::layout( - topTree, - { - .commit = Layout::Commit::YES, - .knownSize = topRect.size().cast>(), - .position = topRect.topStart(), - .availableSpace = topRect.size(), - .containingBlock = topRect.size(), - } - ); - Layout::paint(topTree.root, stack); - - // MARK: Bottom ------------------------------------------------------------ - - auto bottomRect = RectPx::fromTwoPoint( - bottomLeftMarginCornerRect.topEnd(), - bottomRightMarginCornerRect.bottomStart() - ); - - auto bottomBox = Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::BOTTOM)); - bottomBox.add(Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::BOTTOM_LEFT))); - bottomBox.add(Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::BOTTOM_CENTER))); - bottomBox.add(Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::BOTTOM_RIGHT))); - - Layout::Tree bottomTree{ - .root = std::move(bottomBox), - .viewport = Layout::Viewport{.small = bottomRect.size()} - }; - - Layout::layout( - bottomTree, - { - .commit = Layout::Commit::YES, - .knownSize = bottomRect.size().cast>(), - .position = bottomRect.topStart(), - .availableSpace = bottomRect.size(), - .containingBlock = bottomRect.size(), - } - ); - - Layout::paint(bottomTree.root, stack); - - // MARK: Left -------------------------------------------------------------- - auto leftRect = RectPx::fromTwoPoint( - topLeftMarginCornerRect.bottomEnd(), - bottomLeftMarginCornerRect.topStart() - ); - - auto leftBox = Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::LEFT)); - leftBox.add(Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::LEFT_TOP))); - leftBox.add(Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::LEFT_MIDDLE))); - leftBox.add(Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::LEFT_BOTTOM))); - - Layout::Tree leftTree{ - .root = std::move(leftBox), - .viewport = Layout::Viewport{.small = leftRect.size()} - }; - - Layout::layout( - leftTree, - { - .commit = Layout::Commit::YES, - .knownSize = leftRect.size().cast>(), - .position = leftRect.topStart(), - .availableSpace = leftRect.size(), - .containingBlock = leftRect.size(), - } - ); - - Layout::paint(leftTree.root, stack); - - // MARK: Right ------------------------------------------------------------- - - auto rightRect = RectPx::fromTwoPoint( - topRightMarginCornerRect.bottomEnd(), - bottomRightMarginCornerRect.topStart() - ); - - auto rightBox = Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::RIGHT)); - rightBox.add(Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::RIGHT_TOP))); - rightBox.add(Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::RIGHT_MIDDLE))); - rightBox.add(Layout::buildForPseudoElement(pageStyle.area(Style::PageArea::RIGHT_BOTTOM))); - - Layout::Tree rightTree{ - .root = std::move(rightBox), - .viewport = Layout::Viewport{.small = rightRect.size()} - }; - - Layout::layout( - rightTree, - { - .commit = Layout::Commit::YES, - .knownSize = rightRect.size().cast>(), - .position = rightRect.topStart(), - .availableSpace = rightRect.size(), - .containingBlock = rightRect.size(), - } - ); - - Layout::paint(rightTree.root, stack); -} - -Vec> print(Markup::Document const &dom, Style::Media const &media) { - Style::StyleBook stylebook; - stylebook.add( - fetchStylesheet("bundle://vaev-driver/html.css"_url, Style::Origin::USER_AGENT) - .take("user agent stylesheet not available") - ); - stylebook.add( - fetchStylesheet("bundle://vaev-driver/print.css"_url, Style::Origin::USER_AGENT) - .take("print stylesheet not available") - ); - - _collectStyle(dom, stylebook); - - Style::Computer computer{media, stylebook}; - - // MARK: Page and Margins -------------------------------------------------- - - Vec> pages; - - Layout::Resolver resolver{}; - Style::Page page{.name = ""s, .number = pages.len(), .blank = false}; - auto pageStyle = computer.computeFor(page); - RectPx pageRect{ - media.width / Px{media.resolution.toDppx()}, - media.height / Px{media.resolution.toDppx()} - }; - - auto scaleMatrix = Math::Trans2f::makeScale(media.resolution.toDppx()); - auto pageScene = makeStrong(pageRect.size().cast(), scaleMatrix); - - InsetsPx 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), - }; - - RectPx pageContent = pageRect.shrink(pageMargin); - - _paintMargins(*pageStyle, pageRect, pageContent, *pageScene); - - // MARK: Page Content ------------------------------------------------------ - - Layout::Viewport vp{ - .small = pageContent.size(), - }; - - Layout::Tree contentTree = { - Layout::build(computer, dom), - vp, - }; - - Layout::layout( - contentTree, - { - .commit = Layout::Commit::YES, - .knownSize = {pageContent.width, NONE}, - .position = pageContent.topStart(), - .availableSpace = pageContent.size(), - .containingBlock = pageContent.size(), - } - ); - - Layout::paint(contentTree.root, *pageScene); - pageScene->prepare(); - pages.pushBack(pageScene); - - return pages; -} - } // namespace Vaev::Driver diff --git a/src/web/vaev-driver/render.h b/src/web/vaev-driver/render.h index 7fa2874..35b93ff 100644 --- a/src/web/vaev-driver/render.h +++ b/src/web/vaev-driver/render.h @@ -17,6 +17,4 @@ struct RenderResult { RenderResult render(Markup::Document const &dom, Style::Media const &media, Layout::Viewport viewport); -Vec> print(Markup::Document const &dom, Style::Media const &media); - } // namespace Vaev::Driver diff --git a/src/web/vaev-driver/res/print.css b/src/web/vaev-driver/res/print.css index 0ffaeff..60b6fe7 100644 --- a/src/web/vaev-driver/res/print.css +++ b/src/web/vaev-driver/res/print.css @@ -20,12 +20,14 @@ text-align: left; vertical-align: middle; flex-grow: 1; + content: var(-vaev-datetime); } @top-center { text-align: center; vertical-align: middle; flex-grow: 1; + content: var(-vaev-title); } @top-right { @@ -106,6 +108,7 @@ text-align: left; vertical-align: middle; flex-grow: 1; + content: var(-vaev-url); } @bottom-center { @@ -118,7 +121,6 @@ text-align: right; vertical-align: middle; flex-grow: 1; - } @bottom-right-corner { diff --git a/src/web/vaev-layout/builder.cpp b/src/web/vaev-layout/builder.cpp index 0c90ca1..bbccddb 100644 --- a/src/web/vaev-layout/builder.cpp +++ b/src/web/vaev-layout/builder.cpp @@ -320,7 +320,6 @@ Box buildForPseudoElement(Strong style) { auto prose = makeStrong(proseStyle); if (style->content) { - logDebug("content: '{}'", style->content); prose->append(style->content.str()); return {style, fontFace, prose}; } diff --git a/src/web/vaev-style/computed.h b/src/web/vaev-style/computed.h index bb9e4bf..c0de63c 100644 --- a/src/web/vaev-style/computed.h +++ b/src/web/vaev-style/computed.h @@ -111,6 +111,17 @@ struct Computed { e(" variables: {}", variables); e(")"); } + + void setCustomProp(Str varName, Css::Content value) { + variables.cow().put(varName, value); + } + + Css::Content getCustomProp(Str varName) const { + auto value = variables->access(varName); + if (value) + return *value; + return {}; + } }; } // namespace Vaev::Style diff --git a/src/web/vaev-style/computer.cpp b/src/web/vaev-style/computer.cpp index 679592f..609469e 100644 --- a/src/web/vaev-style/computer.cpp +++ b/src/web/vaev-style/computer.cpp @@ -111,8 +111,8 @@ Strong Computer::computeFor(Computed const &parent, Markup::Element co return _evalCascade(parent, matchingRules); } -Strong Computer::computeFor(Page const &page) { - auto computed = makeStrong(); +Strong Computer::computeFor(Computed const &parent, Page const &page) { + auto computed = makeStrong(parent); for (auto const &sheet : _styleBook.styleSheets) for (auto const &rule : sheet.rules) diff --git a/src/web/vaev-style/computer.h b/src/web/vaev-style/computer.h index 83e0449..4c5d285 100644 --- a/src/web/vaev-style/computer.h +++ b/src/web/vaev-style/computer.h @@ -21,7 +21,7 @@ struct Computer { Strong computeFor(Computed const &parent, Markup::Element const &el); - Strong computeFor(Page const &page); + Strong computeFor(Computed const &parent, Page const &page); }; } // namespace Vaev::Style diff --git a/src/web/vaev-style/media.h b/src/web/vaev-style/media.h index 3cd0d6b..1b885f8 100644 --- a/src/web/vaev-style/media.h +++ b/src/web/vaev-style/media.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -32,7 +33,7 @@ struct Media { /// 4.4. Orientation: the orientation feature /// https://drafts.csswg.org/mediaqueries/#orientation - Orientation orientation; + Print::Orientation orientation; // 5. MARK: Display Quality Media Features @@ -307,7 +308,7 @@ using AspectRatioFeature = RangeFeature<"aspect-ratio", Number, &Media::aspectRa /// 4.4. Device Height: the device-height feature /// https://drafts.csswg.org/mediaqueries/#orientation -using OrientationFeature = DiscreteFeature<"orientation", Orientation, &Media::orientation>; +using OrientationFeature = DiscreteFeature<"orientation", Print::Orientation, &Media::orientation>; // 5. MARK: Display Quality Media Features diff --git a/src/web/vaev-style/page.h b/src/web/vaev-style/page.h index 2492826..241bb0a 100644 --- a/src/web/vaev-style/page.h +++ b/src/web/vaev-style/page.h @@ -43,15 +43,19 @@ struct Page { }; struct PageComputedStyle { - Strong style = makeStrong(Computed::initial()); - Array, static_cast(PageArea::_LEN)> _areas = { -#define ITER(...) makeStrong(Computed::initial()), - FOREACH_PAGE_AREA(ITER) -#undef ITER - }; + using Areas = Array, toUnderlyingType(PageArea::_LEN)>; + + Strong style; + Areas _areas; + + PageComputedStyle(Computed const &initial) + : style(makeStrong(initial)), + _areas(Areas::fill([&](...) { + return makeStrong(initial); + })) {} Strong area(PageArea margin) const { - return _areas[static_cast(margin)]; + return _areas[toUnderlyingType(margin)]; } }; diff --git a/src/web/vaev-style/styles.cpp b/src/web/vaev-style/styles.cpp index f25327d..da7b1fc 100644 --- a/src/web/vaev-style/styles.cpp +++ b/src/web/vaev-style/styles.cpp @@ -69,7 +69,7 @@ void DeferredProp::apply(Computed const &parent, Computed &c) const { Css::Sst decl{Css::Sst::DECL}; decl.token = Css::Token::ident(propName); Cursor cursor = value; - _expandContent(cursor, c.variables.cow(), decl.content); + _expandContent(cursor, *c.variables, decl.content); // Parse the expanded content Res computed = parseDeclaration(decl, false); diff --git a/src/web/vaev-style/styles.h b/src/web/vaev-style/styles.h index bb34ecf..f51359f 100644 --- a/src/web/vaev-style/styles.h +++ b/src/web/vaev-style/styles.h @@ -2642,7 +2642,7 @@ struct CustomProp { static constexpr Str name() { return "custom prop"; } void apply(Computed &c) const { - c.variables.cow().put(varName, value); + c.setCustomProp(varName, value); } void repr(Io::Emit &e) const { diff --git a/src/web/vaev-style/values.cpp b/src/web/vaev-style/values.cpp index 5fbf414..8871ff2 100644 --- a/src/web/vaev-style/values.cpp +++ b/src/web/vaev-style/values.cpp @@ -807,14 +807,14 @@ Res ValueParser::parse(Cursor &c) { // MARK: Orientation // https://drafts.csswg.org/mediaqueries/#orientation -Res ValueParser::parse(Cursor &c) { +Res ValueParser::parse(Cursor &c) { if (c.ended()) return Error::invalidData("unexpected end of input"); if (c.skip(Css::Token::ident("portrait"))) - return Ok(Orientation::PORTRAIT); + return Ok(Print::Orientation::PORTRAIT); else if (c.skip(Css::Token::ident("landscape"))) - return Ok(Orientation::LANDSCAPE); + return Ok(Print::Orientation::LANDSCAPE); else return Error::invalidData("expected orientation"); } diff --git a/src/web/vaev-style/values.h b/src/web/vaev-style/values.h index 1a7478d..faf5e9a 100644 --- a/src/web/vaev-style/values.h +++ b/src/web/vaev-style/values.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -283,8 +284,8 @@ struct ValueParser { }; template <> -struct ValueParser { - static Res parse(Cursor &c); +struct ValueParser { + static Res parse(Cursor &c); }; template <> diff --git a/src/web/vaev-view/dialog.cpp b/src/web/vaev-view/dialog.cpp index 3ad03f2..f885b7d 100644 --- a/src/web/vaev-view/dialog.cpp +++ b/src/web/vaev-view/dialog.cpp @@ -1,47 +1,13 @@ #include -#include +#include #include "dialog.h" namespace Vaev::View { -Style::Media _constructMedia(Print::Settings const &settings) { - return { - .type = MediaType::SCREEN, - .width = Px{settings.paper.width}, - .height = Px{settings.paper.height}, - .aspectRatio = settings.paper.width / (f64)settings.paper.height, - .orientation = Orientation::LANDSCAPE, - - .resolution = Resolution{settings.scale, Resolution::X}, - .scan = Scan::PROGRESSIVE, - .grid = false, - .update = Update::FAST, - - .overflowBlock = OverflowBlock::SCROLL, - .overflowInline = OverflowInline::SCROLL, - - .color = 8, - .colorIndex = 0, - .monochrome = 0, - .colorGamut = ColorGamut::SRGB, - .pointer = Pointer::FINE, - .hover = Hover::HOVER, - .anyPointer = Pointer::FINE, - .anyHover = Hover::HOVER, - - .prefersReducedMotion = ReducedMotion::NO_PREFERENCE, - .prefersReducedTransparency = ReducedTransparency::NO_PREFERENCE, - .prefersContrast = Contrast::NO_PREFERENCE, - .forcedColors = Colors::NONE, - .prefersColorScheme = ColorScheme::LIGHT, - .prefersReducedData = ReducedData::NO_PREFERENCE, - }; -} - Ui::Child printDialog(Strong dom) { return Kr::printDialog([dom](Print::Settings const &settings) { - return Driver::print(*dom, _constructMedia(settings)); + return Driver::print(*dom, settings); }); } diff --git a/src/web/vaev-view/view.cpp b/src/web/vaev-view/view.cpp index 23d0003..a825333 100644 --- a/src/web/vaev-view/view.cpp +++ b/src/web/vaev-view/view.cpp @@ -18,7 +18,7 @@ struct View : public Ui::View { .width = Px{viewport.width}, .height = Px{viewport.height}, .aspectRatio = viewport.width / (f64)viewport.height, - .orientation = Orientation::LANDSCAPE, + .orientation = Print::Orientation::LANDSCAPE, .resolution = Resolution::fromDpi(96), .scan = Scan::PROGRESSIVE,