1mod collect;
4mod finalize;
5mod run;
6
7use comemo::{Track, Tracked, TrackedMut};
8use ecow::EcoVec;
9use typst_library::diag::SourceResult;
10use typst_library::engine::{Engine, Route, Sink, Traced};
11use typst_library::foundations::{Content, StyleChain};
12use typst_library::introspection::{
13 Introspector, Locator, LocatorLink, ManualPageCounter, SplitLocator, TagElem,
14};
15use typst_library::layout::{FrameItem, Point};
16use typst_library::model::DocumentInfo;
17use typst_library::routines::{Arenas, Pair, RealizationKind};
18use typst_library::{Library, World};
19use typst_utils::{LazyHash, Protected};
20
21use self::collect::{Item, collect};
22use self::finalize::finalize;
23use self::run::{LayoutedPage, layout_blank_page, layout_page_run};
24use crate::{Page, PagedDocument};
25
26#[typst_macros::time(name = "layout document")]
33pub fn layout_document(
34 engine: &mut Engine,
35 content: &Content,
36 styles: StyleChain,
37) -> SourceResult<PagedDocument> {
38 layout_document_impl(
39 engine.world,
40 engine.library,
41 engine.introspector.into_raw(),
42 engine.traced,
43 TrackedMut::reborrow_mut(&mut engine.sink),
44 engine.route.track(),
45 content,
46 styles,
47 )
48}
49
50#[comemo::memoize]
52#[allow(clippy::too_many_arguments)]
53fn layout_document_impl(
54 world: Tracked<dyn World + '_>,
55 library: &LazyHash<Library>,
56 introspector: Tracked<dyn Introspector + '_>,
57 traced: Tracked<Traced>,
58 sink: TrackedMut<Sink>,
59 route: Tracked<Route>,
60 content: &Content,
61 styles: StyleChain,
62) -> SourceResult<PagedDocument> {
63 layout_document_common(
64 library,
65 world,
66 introspector,
67 traced,
68 sink,
69 route,
70 content,
71 Locator::root(),
72 styles,
73 )
74}
75
76#[typst_macros::time(name = "layout document")]
78pub fn layout_document_for_bundle(
79 engine: &mut Engine,
80 content: &Content,
81 locator: Locator,
82 styles: StyleChain,
83) -> SourceResult<PagedDocument> {
84 layout_document_for_bundle_impl(
85 engine.world,
86 engine.library,
87 engine.introspector.into_raw(),
88 engine.traced,
89 TrackedMut::reborrow_mut(&mut engine.sink),
90 engine.route.track(),
91 content,
92 locator.track(),
93 styles,
94 )
95}
96
97#[comemo::memoize]
99#[allow(clippy::too_many_arguments)]
100fn layout_document_for_bundle_impl(
101 world: Tracked<dyn World + '_>,
102 library: &LazyHash<Library>,
103 introspector: Tracked<dyn Introspector + '_>,
104 traced: Tracked<Traced>,
105 sink: TrackedMut<Sink>,
106 route: Tracked<Route>,
107 content: &Content,
108 locator: Tracked<Locator>,
109 styles: StyleChain,
110) -> SourceResult<PagedDocument> {
111 let link = LocatorLink::new(locator);
112 layout_document_common(
113 library,
114 world,
115 introspector,
116 traced,
117 sink,
118 route,
119 content,
120 Locator::link(&link),
121 styles,
122 )
123}
124
125#[allow(clippy::too_many_arguments)]
128fn layout_document_common(
129 library: &LazyHash<Library>,
130 world: Tracked<dyn World + '_>,
131 introspector: Tracked<dyn Introspector + '_>,
132 traced: Tracked<Traced>,
133 sink: TrackedMut<Sink>,
134 route: Tracked<Route>,
135 content: &Content,
136 locator: Locator,
137 styles: StyleChain,
138) -> SourceResult<PagedDocument> {
139 let introspector = Protected::from_raw(introspector);
140 let mut locator = locator.split();
141 let mut engine = Engine {
142 library,
143 world,
144 introspector,
145 traced,
146 sink,
147 route: Route::extend(route).unnested(),
148 };
149
150 let styles = styles.to_map().outside();
153 let styles = StyleChain::new(&styles);
154 let arenas = Arenas::default();
155
156 let mut info = DocumentInfo::default();
157 info.populate(styles);
158 info.populate_locale(styles);
159
160 let mut children = (engine.library.routines.realize)(
161 RealizationKind::Document { info: &mut info },
162 &mut engine,
163 &mut locator,
164 &arenas,
165 content,
166 styles,
167 )?;
168
169 let pages = layout_pages(&mut engine, &mut children, &mut locator, styles)?;
170
171 Ok(PagedDocument::new(pages, info))
172}
173
174fn layout_pages<'a>(
176 engine: &mut Engine,
177 children: &'a mut [Pair<'a>],
178 locator: &mut SplitLocator<'a>,
179 styles: StyleChain<'a>,
180) -> SourceResult<EcoVec<Page>> {
181 let items = collect(children, locator, styles);
183
184 let mut runs = engine.parallelize(
186 items.iter().filter_map(|item| match item {
187 Item::Run(children, initial, locator) => {
188 Some((children, initial, locator.relayout()))
189 }
190 _ => None,
191 }),
192 |engine, (children, initial, locator)| {
193 layout_page_run(engine, children, locator, *initial)
194 },
195 );
196
197 let mut pages = EcoVec::new();
198 let mut tags = vec![];
199 let mut counter = ManualPageCounter::new();
200
201 for item in &items {
204 match item {
205 Item::Run(..) => {
206 let layouted = runs.next().unwrap()?;
207 for layouted in layouted {
208 let page = finalize(engine, &mut counter, &mut tags, layouted)?;
209 pages.push(page);
210 }
211 }
212 Item::Parity(parity, initial, locator) => {
213 if !parity.matches(pages.len()) {
214 continue;
215 }
216
217 let layouted = layout_blank_page(engine, locator.relayout(), *initial)?;
218 let page = finalize(engine, &mut counter, &mut tags, layouted)?;
219 pages.push(page);
220 }
221 Item::Tags(items) => {
222 tags.extend(
223 items
224 .iter()
225 .filter_map(|(c, _)| c.to_packed::<TagElem>())
226 .map(|elem| elem.tag.clone()),
227 );
228 }
229 }
230 }
231
232 if !tags.is_empty() {
234 let last = pages.make_mut().last_mut().unwrap();
235 let pos = Point::with_y(last.frame.height());
236 last.frame
237 .push_multiple(tags.into_iter().map(|tag| (pos, FrameItem::Tag(tag))));
238 }
239
240 Ok(pages)
241}