summaryrefslogtreecommitdiff
path: root/crates/typst-layout/src/pages/finalize.rs
blob: b16d95699fe62f0d0230be61f135b375815ca1c5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
use typst_library::diag::SourceResult;
use typst_library::engine::Engine;
use typst_library::introspection::{ManualPageCounter, Tag};
use typst_library::layout::{Frame, FrameItem, Page, Point};

use super::LayoutedPage;

/// Piece together the inner page frame and the marginals. We can only do this
/// at the very end because inside/outside margins require knowledge of the
/// physical page number, which is unknown during parallel layout.
pub fn finalize(
    engine: &mut Engine,
    counter: &mut ManualPageCounter,
    tags: &mut Vec<Tag>,
    LayoutedPage {
        inner,
        mut margin,
        binding,
        two_sided,
        header,
        footer,
        background,
        foreground,
        fill,
        numbering,
        supplement,
    }: LayoutedPage,
) -> SourceResult<Page> {
    // If two sided, left becomes inside and right becomes outside.
    // Thus, for left-bound pages, we want to swap on even pages and
    // for right-bound pages, we want to swap on odd pages.
    if two_sided && binding.swap(counter.physical()) {
        std::mem::swap(&mut margin.left, &mut margin.right);
    }

    // Create a frame for the full page.
    let mut frame = Frame::hard(inner.size() + margin.sum_by_axis());

    // Add tags.
    for tag in tags.drain(..) {
        frame.push(Point::zero(), FrameItem::Tag(tag));
    }

    // Add the "before" marginals. The order in which we push things here is
    // important as it affects the relative ordering of introspectable elements
    // and thus how counters resolve.
    if let Some(background) = background {
        frame.push_frame(Point::zero(), background);
    }
    if let Some(header) = header {
        frame.push_frame(Point::with_x(margin.left), header);
    }

    // Add the inner contents.
    frame.push_frame(Point::new(margin.left, margin.top), inner);

    // Add the "after" marginals.
    if let Some(footer) = footer {
        let y = frame.height() - footer.height();
        frame.push_frame(Point::new(margin.left, y), footer);
    }
    if let Some(foreground) = foreground {
        frame.push_frame(Point::zero(), foreground);
    }

    // Apply counter updates from within the page to the manual page counter.
    counter.visit(engine, &frame)?;

    // Get this page's number and then bump the counter for the next page.
    let number = counter.logical();
    counter.step();

    Ok(Page { frame, fill, numbering, supplement, number })
}