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