Skip to content

Commit 7b9da24

Browse files
committed
vaev-engine: Implement page base size computation.
1 parent edb5dc6 commit 7b9da24

File tree

4 files changed

+126
-2
lines changed

4 files changed

+126
-2
lines changed

src/vaev-engine/driver/print.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,35 @@ Pair<RectAu> _computePageBounds(Print::Settings const& settings, Style::Media co
154154
};
155155
}
156156

157+
// https://www.w3.org/TR/css-page-3/#issue-59a903e8
158+
Style::Media _constructMediaForBasePageSize(Gc::Ref<Dom::Document> dom, Print::Settings const& settings) {
159+
auto const media = _constructMedia(settings);
160+
161+
// ----------------- Computing UA page size
162+
InsetsAu uAPageMargin;
163+
if (settings.margins == Print::Margins::DEFAULT) {
164+
uAPageMargin = {resolveAbsoluteLength(Length{0.5, Length::Unit::IN})};
165+
} else if (settings.margins == Print::Margins::CUSTOM) {
166+
uAPageMargin = settings.margins.custom.cast<Au>();
167+
} else if (settings.margins == Print::Margins::MINIMUM) {
168+
uAPageMargin = {};
169+
}
170+
171+
RectAu uAPageRect{
172+
media.width / Au{media.resolution.toDppx()},
173+
media.height / Au{media.resolution.toDppx()}
174+
};
175+
176+
RectAu uAPageContent = uAPageRect.shrink(uAPageMargin);
177+
178+
// ----------------- Computing Page Base Size
179+
auto pageStyleFromUAPage = computePageBaseSize(*dom->styleSheets, media);
180+
auto [basePageRect, _] = _computePageBounds(settings, media, *pageStyleFromUAPage);
181+
return media.withPixelsDimensions(basePageRect.size());
182+
}
183+
157184
export Generator<Print::Page> print(Gc::Ref<Dom::Document> dom, Print::Settings const& settings) {
158-
auto media = _constructMedia(settings);
185+
auto const media = _constructMediaForBasePageSize(dom, settings);
159186

160187
Text::FontBook fontBook;
161188
if (not fontBook.loadAll())
@@ -164,6 +191,7 @@ export Generator<Print::Page> print(Gc::Ref<Dom::Document> dom, Print::Settings
164191
Style::Computer computer{
165192
media, *dom->styleSheets, fontBook
166193
};
194+
167195
computer.build();
168196
computer.styleDocument(*dom);
169197

src/vaev-engine/driver/tests/test-print-paper-dimensions.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ void buildTestCase(Gc::Heap& gc, Gc::Ref<Dom::Document> dom, usize amountOfPages
2424

2525
Karm::StringBuilder contentBuilder;
2626
contentBuilder.append("<html><body>"s);
27+
contentBuilder.append("<div id=\"maybe1\">hi</div>"s);
28+
contentBuilder.append("<div id=\"maybe2\">hi</div>"s);
2729
for (usize i = 0; i < amountOfPages; ++i) {
2830
contentBuilder.append("<div id=\"break\">hi</div>"s);
2931
}
@@ -187,4 +189,49 @@ test$("page-left-right") {
187189
return Ok();
188190
}
189191

192+
test$("page-media-query") {
193+
Gc::Heap gc;
194+
195+
auto dom = gc.alloc<Dom::Document>(Mime::Url());
196+
197+
usize const amountOfPages = 1;
198+
199+
Karm::StringBuilder styleBuilder;
200+
201+
// we use UA size and get bage pase sz of 900px
202+
styleBuilder.append("@media (max-width: 800px) { @page { size: 900px; } }\n"s);
203+
204+
// base page size is used to solve media queries, and sets new page sizes without changing the media
205+
styleBuilder.append(
206+
"@media (width: 900px) { @page { size: 1000px; } #maybe1 { break-after: page; } }\n"s
207+
);
208+
209+
// this media query should not be used
210+
styleBuilder.append(
211+
"@media (width: 1000px) { @page { size: 1100px; } #maybe1 { break-after: page; } #maybe2 { break-after: page; } }\n"s
212+
);
213+
214+
buildTestCase(gc, dom, amountOfPages, styleBuilder.take());
215+
216+
Print::Settings settings = {
217+
.paper = Print::A4,
218+
.orientation = Print::Orientation::PORTRAIT,
219+
.scale = 1,
220+
};
221+
222+
Vec<Math::Vec2f> expectedPageSizes;
223+
for (usize i = 0; i < amountOfPages + 1; ++i) {
224+
expectedPageSizes.pushBack({1000.0, 1000.0});
225+
}
226+
227+
auto actualPageSizes = printedPaperSizes(dom, settings);
228+
229+
if (not sizesAreEqual(expectedPageSizes, actualPageSizes)) {
230+
logError("expected: {}, actual: {}", expectedPageSizes, actualPageSizes);
231+
expect$(false);
232+
}
233+
234+
return Ok();
235+
}
236+
190237
} // namespace Vaev::Driver::Tests

src/vaev-engine/style/computer.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,35 @@ import :style.stylesheet;
1313

1414
namespace Vaev::Style {
1515

16+
void _evalGuardlessPageRule(Rule const& rule, Media const& media, PageSpecifiedValues& c) {
17+
rule.visit(Visitor{
18+
[&](PageRule const& r) {
19+
if (r.selectors.len() == 0)
20+
r.apply(c);
21+
},
22+
[&](MediaRule const& r) {
23+
if (r.match(media))
24+
for (auto const& subRule : r.rules)
25+
_evalGuardlessPageRule(subRule, media, c);
26+
},
27+
[&](auto const&) {
28+
// Ignore other rule types
29+
},
30+
});
31+
}
32+
33+
export Rc<PageSpecifiedValues> computePageBaseSize(StyleSheetList const& styleBook, Media const& media) {
34+
auto computed = makeRc<PageSpecifiedValues>(SpecifiedValues::initial());
35+
36+
for (auto const& sheet : styleBook.styleSheets)
37+
for (auto const& rule : sheet.rules)
38+
_evalGuardlessPageRule(rule, media, *computed);
39+
40+
return computed;
41+
}
42+
1643
export struct Computer {
17-
Media _media;
44+
Media const _media;
1845
StyleSheetList const& _styleBook;
1946
Text::FontBook& fontBook;
2047
StyleRuleLookup _styleRuleLookup{};

src/vaev-engine/style/media.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ namespace Vaev::Style {
1414

1515
// MARK: Media -----------------------------------------------------------------
1616

17+
// FIXME: media queries not correctly comparing different length units
1718
export struct Media {
1819
/// 2.3. Media Types
1920
/// https://drafts.csswg.org/mediaqueries/#media-types
@@ -35,6 +36,7 @@ export struct Media {
3536

3637
/// 4.4. Orientation: the orientation feature
3738
/// https://drafts.csswg.org/mediaqueries/#orientation
39+
// FIXME: this should not be a field, but a method dependent on width and height
3840
Print::Orientation orientation;
3941

4042
// 5. MARK: Display Quality Media Features
@@ -136,6 +138,26 @@ export struct Media {
136138
Au deviceWidth;
137139
Au deviceHeight;
138140
Number deviceAspectRatio;
141+
142+
Media withPixelsDimensions(Vec2Au newSize) const {
143+
newSize = newSize * Au{resolution.toDppx()};
144+
145+
Media newMedia = *this;
146+
147+
newMedia.width = newSize.x;
148+
newMedia.height = newSize.y;
149+
newMedia.aspectRatio = (f64)newSize.x / (f64)newSize.y;
150+
151+
newMedia.orientation = newMedia.height >= newMedia.width
152+
? Print::Orientation::PORTRAIT
153+
: Print::Orientation::LANDSCAPE;
154+
155+
newMedia.deviceWidth = newMedia.width;
156+
newMedia.deviceHeight = newMedia.height;
157+
newMedia.deviceAspectRatio = newMedia.aspectRatio;
158+
159+
return newMedia;
160+
}
139161
};
140162

141163
// MARK: Media Features --------------------------------------------------------

0 commit comments

Comments
 (0)