1mod collect;
4mod finalize;
5mod run;
6
7use comemo::{Tracked, TrackedMut};
8use typst_library::diag::SourceResult;
9use typst_library::engine::{Engine, Route, Sink, Traced};
10use typst_library::foundations::{Content, StyleChain};
11use typst_library::introspection::{
12 Introspector, Locator, ManualPageCounter, SplitLocator, TagElem,
13};
14use typst_library::layout::{FrameItem, Page, PagedDocument, Point};
15use typst_library::model::DocumentInfo;
16use typst_library::routines::{Arenas, Pair, RealizationKind, Routines};
17use typst_library::World;
18
19use self::collect::{collect, Item};
20use self::finalize::finalize;
21use self::run::{layout_blank_page, layout_page_run, LayoutedPage};
22
23#[typst_macros::time(name = "layout document")]
30pub fn layout_document(
31 engine: &mut Engine,
32 content: &Content,
33 styles: StyleChain,
34) -> SourceResult<PagedDocument> {
35 layout_document_impl(
36 engine.routines,
37 engine.world,
38 engine.introspector,
39 engine.traced,
40 TrackedMut::reborrow_mut(&mut engine.sink),
41 engine.route.track(),
42 content,
43 styles,
44 )
45}
46
47#[comemo::memoize]
49#[allow(clippy::too_many_arguments)]
50fn layout_document_impl(
51 routines: &Routines,
52 world: Tracked<dyn World + '_>,
53 introspector: Tracked<Introspector>,
54 traced: Tracked<Traced>,
55 sink: TrackedMut<Sink>,
56 route: Tracked<Route>,
57 content: &Content,
58 styles: StyleChain,
59) -> SourceResult<PagedDocument> {
60 let mut locator = Locator::root().split();
61 let mut engine = Engine {
62 routines,
63 world,
64 introspector,
65 traced,
66 sink,
67 route: Route::extend(route).unnested(),
68 };
69
70 let styles = styles.to_map().outside();
73 let styles = StyleChain::new(&styles);
74
75 let arenas = Arenas::default();
76 let mut info = DocumentInfo::default();
77 let mut children = (engine.routines.realize)(
78 RealizationKind::LayoutDocument(&mut info),
79 &mut engine,
80 &mut locator,
81 &arenas,
82 content,
83 styles,
84 )?;
85
86 let pages = layout_pages(&mut engine, &mut children, &mut locator, styles)?;
87 let introspector = Introspector::paged(&pages);
88
89 Ok(PagedDocument { pages, info, introspector })
90}
91
92fn layout_pages<'a>(
94 engine: &mut Engine,
95 children: &'a mut [Pair<'a>],
96 locator: &mut SplitLocator<'a>,
97 styles: StyleChain<'a>,
98) -> SourceResult<Vec<Page>> {
99 let items = collect(children, locator, styles);
101
102 let mut runs = engine.parallelize(
104 items.iter().filter_map(|item| match item {
105 Item::Run(children, initial, locator) => {
106 Some((children, initial, locator.relayout()))
107 }
108 _ => None,
109 }),
110 |engine, (children, initial, locator)| {
111 layout_page_run(engine, children, locator, *initial)
112 },
113 );
114
115 let mut pages = vec![];
116 let mut tags = vec![];
117 let mut counter = ManualPageCounter::new();
118
119 for item in &items {
122 match item {
123 Item::Run(..) => {
124 let layouted = runs.next().unwrap()?;
125 for layouted in layouted {
126 let page = finalize(engine, &mut counter, &mut tags, layouted)?;
127 pages.push(page);
128 }
129 }
130 Item::Parity(parity, initial, locator) => {
131 if !parity.matches(pages.len()) {
132 continue;
133 }
134
135 let layouted = layout_blank_page(engine, locator.relayout(), *initial)?;
136 let page = finalize(engine, &mut counter, &mut tags, layouted)?;
137 pages.push(page);
138 }
139 Item::Tags(items) => {
140 tags.extend(
141 items
142 .iter()
143 .filter_map(|(c, _)| c.to_packed::<TagElem>())
144 .map(|elem| elem.tag.clone()),
145 );
146 }
147 }
148 }
149
150 if !tags.is_empty() {
152 let last = pages.last_mut().unwrap();
153 let pos = Point::with_y(last.frame.height());
154 last.frame
155 .push_multiple(tags.into_iter().map(|tag| (pos, FrameItem::Tag(tag))));
156 }
157
158 Ok(pages)
159}